Python 数据处理库 pandas
核心数据结构
pandas最核心的就是Series
和DataFrame
两个数据结构。
名称 | 维度 | 说明 |
---|---|---|
Series | 1维 | 带有标签的同构类型数组 |
DataFrame | 2维 | 表格结构,带有标签,大小可变,且可以包含异构的数据列 |
DataFrame可以看做是Series的容器,即:一个DataFrame中可以包含若干个Series。
series
由于Series是一堆结构的数据,我们可以直接通过数组来创建这种数据,像这样:
import pandas as pd import numpy as np series1 = pd.Series([1, 2, 3, 4]) print("series1:\n{}\n".format(series1)) # series1: # 0 1 # 1 2 # 2 3 # 3 4 # dtype: int64
- 输出的最后一行是Series中数据的类型,这里的数据都是int64类型的。
- 数据在第二列输出,第一列是数据的索引
我们分别打印出Series中的数据和索引
print("series1.values: {}\n".format(series1.values)) print("series1.index: {}\n".format(series1.index)) # series1.values: [1 2 3 4] # series1.index: RangeIndex(start=0, stop=4, step=1)
我们可以指定索引的类型,例如字符串
series2 = pd.Series([1, 2, 3, 4, 5, 6, 7], index=["C", "D", "E", "F", "G", "A", "B"]) print("series2:\n{}\n".format(series2)) print("E is {}\n".format(series2["E"])) # series2: # C 1 # D 2 # E 3 # F 4 # G 5 # A 6 # B 7 # dtype: int64 # E is 3
DataFrame
通过Numpy接口来创建一个4x4的矩阵,以此来创建DataFrame
1 import pandas as pd 2 import numpy as np 3 4 df1 = pd.DataFrame(np.arange(16).reshape(4,4)) 5 print("df1:\n{}\n".format(df1)) 6 7 # df1: 8 # 0 1 2 3 9 # 0 0 1 2 3 10 # 1 4 5 6 7 11 # 2 8 9 10 11 12 # 3 12 13 14 15
默认的索引和列名都是[0,N-1]的形式,同样我们可以指定列名和索引,
1 import pandas as pd 2 import numpy as np 3 4 df2 = pd.DataFrame(np.arange(16).reshape(4,4), 5 columns=["column1", "column2", "column3", "column4"], 6 index=["a", "b", "c", "d"]) 7 print("df2:\n{}\n".format(df2)) 8 9 # df2: 10 # column1 column2 column3 column4 11 # a 0 1 2 3 12 # b 4 5 6 7 13 # c 8 9 10 11 14 # d 12 13 14 15
我们也可以指定结构来创建DataFrame
1 import pandas as pd 2 import numpy as np 3 4 df3 = pd.DataFrame({"note" : ["C", "D", "E", "F", "G", "A", "B"], 5 "weekday": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}) 6 print("df3:\n{}\n".format(df3)) 7 8 # df3: 9 # note weekday 10 # 0 C Mon 11 # 1 D Tue 12 # 2 E Wed 13 # 3 F Thu 14 # 4 G Fri 15 # 5 A Sat 16 # 6 B Sun
注意:
-
DataFrame的不同列可以是不同的数据类型
-
如果以Series数组来创建DataFrame,每个Series将成为一行,而不是一列
1 import pandas as pd 2 import numpy as np 3 4 noteSeries = pd.Series(["C", "D", "E", "F", "G", "A", "B"], 5 index=[1, 2, 3, 4, 5, 6, 7]) 6 weekdaySeries = pd.Series(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 7 index=[1, 2, 3, 4, 5, 6, 7]) 8 df4 = pd.DataFrame([noteSeries, weekdaySeries]) 9 print("df4:\n{}\n".format(df4)) 10 11 # df4: 12 # 1 2 3 4 5 6 7 13 # 0 C D E F G A B 14 # 1 Mon Tue Wed Thu Fri Sat Sun
我们还可以“添加”或“删除”列数据
1 import pandas as pd 2 import numpy as np 3 4 df3 = pd.DataFrame({"note" : ["C", "D", "E", "F", "G", "A", "B"], 5 "weekday": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}) 6 df3["No."] = pd.Series([1, 2, 3, 4, 5, 6, 7]) 7 print("df3:\n{}\n".format(df3)) 8 9 del df3["weekday"] 10 print("df3:\n{}\n".format(df3)) 11 12 # df3: 13 # note weekday No. 14 # 0 C Mon 1 15 # 1 D Tue 2 16 # 2 E Wed 3 17 # 3 F Thu 4 18 # 4 G Fri 5 19 # 5 A Sat 6 20 # 6 B Sun 7 21 22 # df3: 23 # note No. 24 # 0 C 1 25 # 1 D 2 26 # 2 E 3 27 # 3 F 4 28 # 4 G 5 29 # 5 A 6 30 # 6 B 7
index对象与数据访问
同样可以通过索引来获取DataFrame的行和列
1 print("df3.columns\n{}\n".format(df3.columns)) 2 print("df3.index\n{}\n".format(df3.index)) 3 4 # df3.columns 5 # Index(['note', 'No.'], dtype='object') 6 7 # df3.index 8 # RangeIndex(start=0, stop=7, step=1)
注意:
-
Index并非集合,因此其中可以包含重复的数据
-
Index对象的值是不可以改变,因此可以通过它安全的访问数据
DataFrane提供了下面两个操作符来访问其中的数据
-
loc
:通过行和列的索引来访问数据 -
iloc
:通过行和列的下标来访问数据
1 print("Note C, D is:\n{}\n".format(df3.loc[[0, 1], "note"])) 2 print("Note C, D is:\n{}\n".format(df3.iloc[[0, 1], 0])) 3 4 # Note C, D is: 5 # 0 C 6 # 1 D 7 # Name: note, dtype: object 8 9 # Note C, D is: 10 # 0 C 11 # 1 D 12 # Name: note, dtype: object
第一行代码访问了行索引为0和1,列索引为“note”的元素,第二行代码访问了行下标为0和1对于df3来说,行索引和行下标刚好是一样的,所以这里都是0和1,但它们却是不同的含义),列下标为0的元素。
文件操作
读取Excel文件
注:要读取Excel文件,还需要安装另外一个库;xlrd
pip install xlrd
1 import pandas as pd 2 import numpy as np 3 4 df1 = pd.read_excel("data/test.xlsx") 5 print("df1:\n{}\n".format(df1)) 6 7 # df1: 8 # C Mon 9 # 0 D Tue 10 # 1 E Wed 11 # 2 F Thu 12 # 3 G Fri 13 # 4 A Sat 14 # 5 B Sun
读取csv文件
1 C,Mon 2 D,Tue 3 E,Wed 4 F,Thu 5 G,Fri 6 A,Sat
1 C|Mon 2 D|Tue 3 E|Wed 4 F|Thu 5 G|Fri 6 A|Sat
读取CSV文件
1 import pandas as pd 2 import numpy as np 3 df2 = pd.read_csv("data/test1.csv") 4 print("df2:\n{}\n".format(df2)) 5 # df3 = pd.read_csv("data/test2.csv", sep="|") 6 # print("df3:\n{}\n".format(df3))
我们可以发现,第二个CSV文件并不是通过逗号分隔的,我们通常指定分隔符的方式来读取这个文件。
read_csv
支持非常多的参数用来调整读取的参数
参数 | 说明 |
---|---|
path | 文件路径 |
sep或者delimiter | 字段分隔符 |
header | 列名的行数,默认是0(第一行) |
index_col | 列号或名称用作结果中的行索引 |
names | 结果的列名称列表 |
skiprows | 从起始位置跳过的行数 |
na_values | 代替NA 的值序列 |
comment | 以行结尾分隔注释的字符 |
parse_dates | 尝试将数据解析为datetime 。默认为False |
keep_date_col | 如果将列连接到解析日期,保留连接的列。默认为False 。 |
converters | 列的转换器 |
dayfirst | 当解析可以造成歧义的日期时,以内部形式存储。默认为False |
data_parser | 用来解析日期的函数 |
nrows | 从文件开始读取的行数 |
iterator | 返回一个TextParser对象,用于读取部分内容 |
chunksize | 指定读取块的大小 |
skip_footer | 文件末尾需要忽略的行数 |
verbose | 输出各种解析输出的信息 |
encoding | 文件编码 |
squeeze | 如果解析的数据只包含一列,则返回一个Series |
thousands | 千数量的分隔符 |
详细的read_csv函数说明请参见这里:pandas.read_csv
处理无效值
主要有两种处理方法:直接忽略这些无效值;或者将无效值替换成有效值。
我们先创建一个包含无效值的数据结构。然后通过pandas.isna
函数来确认哪些值是无效的:
1 import pandas as pd 2 import numpy as np 3 4 df = pd.DataFrame([[1.0, np.nan, 3.0, 4.0], 5 [5.0, np.nan, np.nan, 8.0], 6 [9.0, np.nan, np.nan, 12.0], 7 [13.0, np.nan, 15.0, 16.0]]) 8 9 print("df:\n{}\n".format(df)); 10 print("df:\n{}\n".format(pd.isna(df))) 11 12 # df: 13 # 0 1 2 3 14 # 0 1.0 NaN 3.0 4.0 15 # 1 5.0 NaN NaN 8.0 16 # 2 9.0 NaN NaN 12.0 17 # 3 13.0 NaN 15.0 16.0 18 19 # df: 20 # 0 1 2 3 21 # 0 False True False False 22 # 1 False True True False 23 # 2 False True True False 24 # 3 False True False False
忽略无效值
我们可以通过pandas.DataFrame.dropna
函数抛弃无效值
1 import pandas as pd 2 import numpy as np 3 4 df = pd.DataFrame([[1.0, np.nan, 3.0, 4.0], 5 [5.0, np.nan, np.nan, 8.0], 6 [9.0, np.nan, np.nan, 12.0], 7 [13.0, np.nan, 15.0, 16.0]]) 8 9 print("df.dropna():\n{}\n".format(df.dropna())); 10 11 # df.dropna(): 12 # Empty DataFrame 13 # Columns: [0, 1, 2, 3] 14 # Index: []
对于原先的结构,当无效值全部被抛弃之后,将不再是一个有效的DataFrame,所以才会是以上结果
我们也可以选择抛弃整列都是无效值的那一列:
1 import pandas as pd 2 import numpy as np 3 4 df = pd.DataFrame([[1.0, np.nan, 3.0, 4.0], 5 [5.0, np.nan, np.nan, 8.0], 6 [9.0, np.nan, np.nan, 12.0], 7 [13.0, np.nan, 15.0, 16.0]]) 8 9 print("df.dropna(axis=1,how='all'):\n{}\n".format(df.dropna(axis=1, how='all'))); 10 11 # df.dropna(axis=1, how='all'): 12 # 0 2 3 13 # 0 1.0 3.0 4.0 14 # 1 5.0 NaN 8.0 15 # 2 9.0 NaN 12.0 16 # 3 13.0 15.0 16.0
注:axis=1
表示列的轴。how可以取值’any’或者’all’,默认是前者。
替换无效值
我们也可以通过fillna
函数将无效值替换成为有效值
1 import pandas as pd 2 import numpy as np 3 4 df = pd.DataFrame([[1.0, np.nan, 3.0, 4.0], 5 [5.0, np.nan, np.nan, 8.0], 6 [9.0, np.nan, np.nan, 12.0], 7 [13.0, np.nan, 15.0, 16.0]]) 8 9 print("df:\n{}\n".format(df)); 10 11 print("df.fillna(1):\n{}\n".format(df.fillna(1))); 12 13 # df: 14 # 0 1 2 3 15 # 0 1.0 NaN 3.0 4.0 16 # 1 5.0 NaN NaN 8.0 17 # 2 9.0 NaN NaN 12.0 18 # 3 13.0 NaN 15.0 16.0 19 20 # df.fillna(1): 21 # 0 1 2 3 22 # 0 1.0 1.0 3.0 4.0 23 # 1 5.0 1.0 1.0 8.0 24 # 2 9.0 1.0 1.0 12.0 25 # 3 13.0 1.0 15.0 16.0
将无效值全部替换成同样的数据可能意义不大,因此我们可以指定不同的数据来进行填充。为了便于操作,在填充之前,我们可以先通过rename
方法修改行和列的名称:
1 import pandas as pd 2 import numpy as np 3 4 df = pd.DataFrame([[1.0, np.nan, 3.0, 4.0], 5 [5.0, np.nan, np.nan, 8.0], 6 [9.0, np.nan, np.nan, 12.0], 7 [13.0, np.nan, 15.0, 16.0]]) 8 9 print("df:\n{}\n".format(df)); 10 print("df:\n{}\n".format(pd.isna(df))) 11 12 df.rename(index={0: 'index1', 1: 'index2', 2: 'index3', 3: 'index4'}, 13 columns={0: 'col1', 1: 'col2', 2: 'col3', 3: 'col4'}, 14 inplace=True); 15 df.fillna(value={'col2': 2}, inplace=True) # 把第2列的空值变成2 16 df.fillna(value={'col3': 7}, inplace=True) # 把第3列的空值变成7 17 print("df:\n{}\n".format(df)); 18 19 # df: 20 # 0 1 2 3 21 # 0 1.0 NaN 3.0 4.0 22 # 1 5.0 NaN NaN 8.0 23 # 2 9.0 NaN NaN 12.0 24 # 3 13.0 NaN 15.0 16.0 25 26 # df: 27 # 0 1 2 3 28 # 0 False True False False 29 # 1 False True True False 30 # 2 False True True False 31 # 3 False True False False 32 33 # df: 34 # col1 col2 col3 col4 35 # index1 1.0 2.0 3.0 4.0 36 # index2 5.0 2.0 7.0 8.0 37 # index3 9.0 2.0 7.0 12.0 38 # index4 13.0 2.0 15.0 16.0
处理字符串
Series
的str
字段包含了一系列的函数用来处理字符串。并且,这些函数会自动处理无效值。
1 import pandas as pd 2 3 s1 = pd.Series([' 1', '2 ', ' 3 ', '4', '5']); 4 print("s1.str.rstrip():\n{}\n".format(s1.str.lstrip())) 5 print("s1.str.strip():\n{}\n".format(s1.str.strip())) 6 print("s1.str.isdigit():\n{}\n".format(s1.str.isdigit())) 7 8 # s1.str.rstrip(): 9 # 0 1 10 # 1 2 11 # 2 3 12 # 3 4 13 # 4 5 14 # dtype: object 15 16 # s1.str.strip(): 17 # 0 1 18 # 1 2 19 # 2 3 20 # 3 4 21 # 4 5 22 # dtype: object 23 24 # s1.str.isdigit(): 25 # 0 False 26 # 1 False 27 # 2 False 28 # 3 True 29 # 4 True 30 # dtype: bool
我们还能对字符串进行大写、小写、以及字符串长度的处理。
1 import pandas as pd 2 3 s2 = pd.Series(['Stairway to Heaven', 'Eruption', 'Freebird', 4 'Comfortably Numb', 'All Along the Watchtower']) 5 print("s2.str.lower():\n{}\n".format(s2.str.lower())) 6 print("s2.str.upper():\n{}\n".format(s2.str.upper())) 7 print("s2.str.len():\n{}\n".format(s2.str.len())) 8 9 # s2.str.lower(): 10 # 0 stairway to heaven 11 # 1 eruption 12 # 2 freebird 13 # 3 comfortably numb 14 # 4 all along the watchtower 15 # dtype: object 16 17 # s2.str.upper(): 18 # 0 STAIRWAY TO HEAVEN 19 # 1 ERUPTION 20 # 2 FREEBIRD 21 # 3 COMFORTABLY NUMB 22 # 4 ALL ALONG THE WATCHTOWER 23 # dtype: object 24 25 # s2.str.len(): 26 # 0 18 27 # 1 8 28 # 2 8 29 # 3 16 30 # 4 24 31 # dtype: int64