Python常用库——DataFrame的运算
由于DataFrame的运算内容较多,所以单独拆分一节进行介绍。在这一部分介绍的内容包括:
1 DataFrame与Excel之间的交互 ★★★
前面介绍到DataFrame对应了Excel中的数据表,那首先来介绍下如何把DataFrame的数据实现与Excel之间的互通。在pandas中提供了读取Excel的csv文件和写入到本地csv文件的方法。
写入使用to_csv函数,将pandas中已经建立好的DataFrame输出到本地的csv文件,其主要参数包括:path_or_buf路径,表示将csv文件写入到本地的哪个位置,同时这个地方需要给csv文件进行命名;sep表示不同列之间的分隔符,在Excel部分讲读取csv时,需要指定相应的分隔符,在这里写入到csv文件时需要指定写出时的分隔符;na_rep,对空值替换为指定值;columns指定输出的列有哪些;header,取值True或False,表示是否输出列名;index取值True或False,表示是否输出行索引值。
a_d_i = pd.DataFrame(data=[ ['Chinese','001',79,8], ['Math','001',96,8], ['English','001',64,8], ['Chinese','002',81,7], ['Math','002',72,7], ['English','002',60,7], ['Chinese','003',90,7], ['Math','003',71,7], ["",'003',92,7], ['Chinese','004',83,8], ['Math','004',90,8], ['English','004',84,8], ['Chinese','005',67,6], ['Math','005',75,6], ['English','005',88,6], ],columns=['subject','s_id','score','age']) a_d_i.to_csv("a_d_i.csv",sep=",",header=True,index=False)
在不指定路径的情况下,csv文件会写入到与ipynb文件相同的目录下,由于jupyter的路径的默认设置,所以在这里直接写入到默认文件夹。可以发现已经存在这一文件夹,使用jupyter打开,则可以查看相应的内容,使用逗号分隔不同列,输出列名,不输出索引值。
有写出,自然也有读入,读取csv文件,使用read_csv,其参数包括:路径名,即读取的csv文件所在的位置;sep,用于z指定读取的csv文件的分隔符,除可以使用sep外,还可以使用delimiter设置,需要注意的时,在同时使用sep和delimiter时,sep不起作用;header参数表示csv文件中是否包含列名,如果包含列名,则使用默认值即可,如果不包含列名,将header设置为None。在这里读取下刚写出的csv文件。
a_d_i_2 = pd.read_csv("a_d_i.csv",sep=",") print(a_d_i_2)
输出结果如下
subject s_id score age 0 Chinese 1 79 8 1 Math 1 96 8 2 English 1 64 8 3 Chinese 2 81 7 4 Math 2 72 7 5 English 2 60 7 6 Chinese 3 90 7 7 Math 3 71 7 8 NaN 3 92 7 9 Chinese 4 83 8 10 Math 4 90 8 11 English 4 84 8 12 Chinese 5 67 6 13 Math 5 75 6 14 English 5 88 6
csv文件和txt文件的写入写出是相同的函数,区别仅在于文件的后缀是txt还是csv。
2 DataFrame的简单运算
2.1 DataFrame的基本属性
对DataFrame的基本属性获取,包括:DataFrame的形状、列名和行索引名称。
首先建立一个DataFrame的样例:
a_d_i = pd.DataFrame(data=np.array([[1,2,3,4,5], [10,20,30,40,50], [100,200,300,400,500], [1000,2000,3000,4000,5000]]),index=['a','b','c','d'],columns=['A','B','C','D','E']) print(a_d_i) print(a_d_i.shape) print(a_d_i.columns) print(a_d_i.index)
使用shape函数获取DataFrame的行数、列数;使用columns获取列名;使用index获取行索引名称。上述代码的输出结果如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 (4, 5) Index(['A', 'B', 'C', 'D', 'E'], dtype='object') Index(['a', 'b', 'c', 'd'], dtype='object')
需要说明的是shape输出的是一个元组,元组的第一个元素是行数,元组的第二个元素是列数,基于元组的索引规则即可获得DataFrame的行数(a_d_i.shape[0])或列数(a_d_i.shape[1])。
2.2 基于行或列的汇总统计
除对DataFrame进行整体的认知外,还可以使用类似Series中的函数对DataFrame进行统计,最为常用就是describe。
a_d_i.describe()
使用describe实现对DataFrame的描述性统计,统计指标包括计数(count)、平均值(mean)、方差(std)、最小值(min)、最大值(max)、上四分位数点、中位数、下四分位数点。需要注意的是,describe是按照每列进行汇总的数据。
A B C D E count 4.000000 4.000000 4.000000 4.000000 4.000000 mean 277.750000 555.500000 833.250000 1111.000000 1388.750000 std 483.570315 967.140631 1450.710946 1934.281262 2417.851577 min 1.000000 2.000000 3.000000 4.000000 5.000000 25% 7.750000 15.500000 23.250000 31.000000 38.750000 50% 55.000000 110.000000 165.000000 220.000000 275.000000 75% 325.000000 650.000000 975.000000 1300.000000 1625.000000 max 1000.000000 2000.000000 3000.000000 4000.000000 5000.000000
describe是存在相应参数的,最常用的参数就是percentiles,用于指定计算的分位数点的位置,默认输出上四分位数、中位数和下四分位数。在这里可以修改为各个十分位数点。
a_d_i.describe(percentiles=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9])
输出结果如下
A B C D E count 4.000000 4.000000 4.000000 4.000000 4.000000 mean 277.750000 555.500000 833.250000 1111.000000 1388.750000 std 483.570315 967.140631 1450.710946 1934.281262 2417.851577 min 1.000000 2.000000 3.000000 4.000000 5.000000 10% 3.700000 7.400000 11.100000 14.800000 18.500000 20% 6.400000 12.800000 19.200000 25.600000 32.000000 30% 9.100000 18.200000 27.300000 36.400000 45.500000 40% 28.000000 56.000000 84.000000 112.000000 140.000000 50% 55.000000 110.000000 165.000000 220.000000 275.000000 60% 82.000000 164.000000 246.000000 328.000000 410.000000 70% 190.000000 380.000000 570.000000 760.000000 950.000000 80% 460.000000 920.000000 1380.000000 1840.000000 2300.000000 90% 730.000000 1460.000000 2190.000000 2920.000000 3650.000000 max 1000.000000 2000.000000 3000.000000 4000.000000 5000.000000
除使用describe进行比较完整的统计外,还可以分别使用相应的函数进行单独的统计。
print(a_d_i.max()) print(a_d_i.min()) print(a_d_i.mean()) print(a_d_i.count()) print(a_d_i.sum())
输出结果如下
A 1000 B 2000 C 3000 D 4000 E 5000 dtype: int32 A 1 B 2 C 3 D 4 E 5 dtype: int32 A 277.75 B 555.50 C 833.25 D 1111.00 E 1388.75 dtype: float64 A 4 B 4 C 4 D 4 E 4 dtype: int64 A 1111 B 2222 C 3333 D 4444 E 5555 dtype: int64
上面介绍的是对所有列执行相应的函数,若相对某列执行函数,则可以使用索引将某列选择出来再进行汇总统计。DataFrame可以看做是各列的集合,每一列是Series,把DataFrame中的某一列取出后,按照Series的统计函数进行计算即可。
print(a_d_i['A'].max()) print(a_d_i['A'].min()) print(a_d_i['A'].mean()) print(a_d_i['A'].count()) print(a_d_i['A'].sum())
输出结果如下
1000 1 277.75 4 1111
在这里需要强调的一点是,使用索引获取出的列的数据类型可能不是Series,而是DataFrame。
print(type(a_d_i['A'])) print(type(a_d_i[['A']]))
输出结果如下
<class 'pandas.core.series.Series'> <class 'pandas.core.frame.DataFrame'>
两者的区别在于使用a_d_i['A']获取的是列名为A的一列;a_d_i[['A']]获取的是a_d_i的子集,如之前讲解的字符串、数组等,DataFrame的子集仍然是一个DataFrame。两者是不同的数据类型,所以在部分函数的使用存在差异,在使用时需要注意。
2.3 基于已有列生成新的列 ★★★
2.3.1 基于索引生成新列
常用Excel的同学可能会注意到,除使用Excel实现统计外,另外一项很重要的功能是基于现有的列生成新列,在pandas中也是比较容易实现的。
在前面介绍的是直接插入新的一列,这里介绍的是如何基于已有的列生成新列或更新已有列的值。
print(a_d_i) a_d_i['F'] = a_d_i['A'] + a_d_i['B'] + a_d_i['C'] print(a_d_i)
输出结果如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 6 b 10 20 30 40 50 60 c 100 200 300 400 500 600 d 1000 2000 3000 4000 5000 6000
可以发现已经生成新列F,其数值是ABC三列的和。
在这里,如果不是新的列F,而是已有的列,则会直接更新已有列的数值。如更新D列是ABC三列的数值。
print(a_d_i) a_d_i['D'] = a_d_i['A'] + a_d_i['B'] + a_d_i['C'] print(a_d_i)
除使用列索引的方法直接更新列的数值外,还可以使用行索引的方法进行更新。
print(a_d_i) a_d_i.loc[['a','c'],'F'] = 0 a_d_i.loc[['b'],'F'] = 1 print(a_d_i)
在这里按照行索引的方式进行赋值,对于a、c行的F列赋值为0,对于b行赋值为1,剩余的d行未赋值,所以显示为空。
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 0.0 b 10 20 30 40 50 1.0 c 100 200 300 400 500 0.0 d 1000 2000 3000 4000 5000 NaN
除上述行索引和列索引外,还可以使用布尔索引的方法实现新列的生成。
print(a_d_i) a_d_i.loc[a_d_i['A'] < 100,'F'] = 1 print(a_d_i) a_d_i.loc[a_d_i['A'] >= 100,'F'] = 0 print(a_d_i)
输出结果如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 1.0 b 10 20 30 40 50 1.0 c 100 200 300 400 500 NaN d 1000 2000 3000 4000 5000 NaN A B C D E F a 1 2 3 4 5 1.0 b 10 20 30 40 50 1.0 c 100 200 300 400 500 0.0 d 1000 2000 3000 4000 5000 0.0
a_d_i['A'] >= 100是布尔索引的判断条件,不再使用单独的行索引值,而是通过是否满足相应的条件来判断该行应该赋值多少。
2.3.2 使用eval函数生成新列
上面的索引书写较为麻烦,可以借助eval函数实现上述函数的简化。
print(a_d_i) a_d_i_1 = a_d_i.eval('F=A+B+C',inplace=False) print(a_d_i) print(a_d_i_1) a_d_i.eval('F=A+B+C',inplace=True) print(a_d_i)
eval函数有两个参数,前面的' '内是计算公式,即将ABC三列的值汇总到F列;inplace参数指定了是否替换原DataFrame,如果不进行替换,则可以函数返回的值赋予新的变量进行输出。
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 6 b 10 20 30 40 50 60 c 100 200 300 400 500 600 d 1000 2000 3000 4000 5000 6000 A B C D E F a 1 2 3 4 5 6 b 10 20 30 40 50 60 c 100 200 300 400 500 600 d 1000 2000 3000 4000 5000 6000
使用eval函数的好处在于简化了索引的使用,直接使用列名进行公式输入即可,方便快捷。
2.3.3 使用apply和where生成新列
除索引方法外,还可以使用pandas的函数实现新列的赋值,常用的方法包括apply和where。 在loc中如果对某列赋值,如果是基于不同的数值赋予不同的值,则需要通过多行代码实现,使用where和apply方法则可以通过一行代码实现多样赋值。
print(a_d_i) a_d_i['F'] = np.where(a_d_i['A'] < 100,1,0) print(a_d_i)
上述代码的输出结果如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 1 b 10 20 30 40 50 1 c 100 200 300 400 500 0 d 1000 2000 3000 4000 5000 0
除可以直接赋值新值外,还可以使用DataFrame中已有的数值。
print(a_d_i) a_d_i['F'] = np.where(a_d_i['A'] < 100,1,a_d_i['E']) print(a_d_i)
上述代码的意思是,对于a_d_i的F列,如果A列的数值小于100,则赋值为1,如果大于等于100,则F列的数值等于E列的数值,所以输出结果如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 1 b 10 20 30 40 50 1 c 100 200 300 400 500 500 d 1000 2000 3000 4000 5000 5000
apply是比where更灵活的操作方法,where一般只能实现是否的两种判断结果的输出,而apply则可以实现更多判断结果的输出。apply的优势在于能够跟函数匹配使用实现更复杂的输出,一般来说使用的是lambda的匿名函数,可以将匿名函数理解为是一个临时的函数。
print(a_d_i) a_d_i['F'] = a_d_i.apply(lambda x: 1 if x['A'] < 100 else 0,axis=1) print(a_d_i)
先看计算结果,输出如下
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 1 b 10 20 30 40 50 1 c 100 200 300 400 500 0 d 1000 2000 3000 4000 5000 0
其实现的功能还是根据A列来生成F列的值,如果A列的值小于100,则赋值为1,如果A列的值大于等于100,则赋值为0。来具体讲解下apply的使用,lambda表示声明匿名函数,其等同于def func_name(),后跟的x表示输入参数,在这里表示相应的DataFrame,后跟冒号:,表示后续为函数的表达式,与常用的if判断的区别在于是在这里先说明执行的语句再说明判断条件,赋值为1在满足x['A'] < 100的条件下,不然(else)则赋值为0。axis=1参数表示是对列进行操作。由于是匿名函数,即用即删,用完后该函数就不存在了,不能在其他地方进行调用。同样的由于是匿名函数,所以操作可以更灵活。
print(a_d_i) a_d_i['F'] = a_d_i.apply(lambda x: 1 if x['A'] < 100 else 2 if x['A'] < 1000 else 0,axis=1) print(a_d_i)
使用多层if函数即可实现判断,仍然需要注意,赋值在前,判断条件在后(除最后的else),将函数语句翻译过来即为,如果A列数值小于100,则赋值1,如果大于等于100且小于1000,则赋值2,如果大于等于1000则赋值0。所以输出结果如下:
A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 A B C D E F a 1 2 3 4 5 1 b 10 20 30 40 50 1 c 100 200 300 400 500 2 d 1000 2000 3000 4000 5000 0
3 对DataFrame进行排序、筛选、分类汇总
3.1 实现数据排序功能 ★★
在pandas中使用sort_values函数实现对DataFrame实现排序,sort_values参数包括三个:排序列名、升序或降序排序、是否替代原DataFrame。
a_d_i_1 = a_d_i print("a_d_i_1排序前\n",a_d_i_1) a_d_i_2 = a_d_i_1.sort_values(by='A',ascending=False,inplace=False) print("a_d_i_2排序后\n",a_d_i_2) print("a_d_i_1排序后\n",a_d_i_1)
首先介绍按照单列对DataFrame实现排序,在这里使用的是A列,ascending参数表示是否升序排序,在这里选择False,即为降序排序;inplace参数表示是否要将排序的结果覆盖原DataFrame(a_d_i_1),在这里设置为False,即不替换原DataFrame,输出结果如下:
a_d_i_1排序前 A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000 a_d_i_2排序后 A B C D E d 1000 2000 3000 4000 5000 c 100 200 300 400 500 b 10 20 30 40 50 a 1 2 3 4 5 a_d_i_1排序后 A B C D E a 1 2 3 4 5 b 10 20 30 40 50 c 100 200 300 400 500 d 1000 2000 3000 4000 5000
如果将参数inplace选择为True,则输出结果如下
a_d_i_1排序前 A B C D E a 1 2
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
数据分析入门技术篇