二、pandas基础
2.1 文件的读取和写入
2.1.1 文件读取
在pandas中可以读取许多格式的文件,我们主要介绍读取csv,excel,txt文件(需要安装xlrd
,xlwt
,openpyxl
三个包,且xlrd版本不能高于2.0.0)
- 对于csv文件
df_csv = pd.read_csv('E:/pandas/data/my_csv.csv')
print(df_csv)
显示的结果为:
- 对于txt文件
df_txt = pd.read_table('E:/pandas/data/my_table.txt')
print(df_txt)
显示结果为:
- 对于Excel文件
df_excel = pd.read_excel('E:/pandas/data/my_excel.xlsx')
print(df_excel)
显示结果为:
而在文件读取中,我们有一些常用的公共参数,例如:header = None
表示第一行不作为列名;index_col
表示把某一列或几列作为索引;usecols
表示读取列的集合,默认读取所有的列,parse_dates
表示需要转化为时间的列,nrows
表示读取的数据行数。这些参数在上述的三个函数里都可以使用。
例:
print(pd.read_table('E:/pandas/data/my_table.txt', header=None))
显示结果为:
print(pd.read_csv('E:/pandas/data/my_csv.csv', index_col=['col1', 'col2']))
显示结果为:
print(pd.read_table('E:/pandas/data/my_table.txt', usecols=['col1', 'col2']))
显示结果为:
print(pd.read_csv('E:/pandas/data/my_csv.csv', parse_dates=['col5']))
显示结果为:
print(pd.read_excel('E:/pandas/data/my_excel.xlsx', nrows=2))
显示结果为:
我们在读取txt文件时,经常遇到分隔符非空格的情况,read_table有一个分割参数sep,它使得用户可以自定义分割符号,进行txt数据的读取。例如,下面的读取的表以||||为分割:
- 原本的形式:
print(pd.read_table('E:/pandas/data/my_table_special_sep.txt'))
显示结果为:
上方的结果显然是不理想的,这个时候我们则可以使用sep
,同时指定引擎为Python
print(pd.read_table('E:/pandas/data/my_table_special_sep.txt', sep=' |||| ', engine='python'))
显示结果为:
**注意!sep
是正则参数,我们需要对|进行转义,只有将其转义为|才能正确读取到结果。`
2.1.2 数据写入
一般在数据的写入中,最常用的操作是将index
设置为False
,尤其是当索引没有特殊意义的时候,这样的操作可以把索引在保存到时候去除
df_csv.to_csv('E:/pandas/data/my_csv_saved.csv', index=False)
df_excel.to_excel('E:/pandas/data/my_excel_saved.xlsx', index=False)
pandas
中没有定义to_table
函数,但是to_csv
可以保存为txt
文件,并且允许自定义分隔符,常用制表符\t
分割:
df_txt.to_csv('E:/pandas/data/my_txt_saved.txt', sep='\t', index=False)
而如果我们想要把表格快速转换为markdown或latex语言,我们可以使用to_markdown
和to_latex
函数,此时需要安装tabulate
包。
print(df_csv.to_markdown())
print(df_csv.to_latex())
2.2 基本数据结构
2.2.1 Series
Series
一般由四个部分组成,分别是序列的值data、索引index、存储类型dtype、序列的名字name。其中,索引也可以指定它的名字,默认为空。
s = pd.Series(data = [100, 'a', {'dic1':5}],
index = pd.Index(['id1', 20, 'third'], name='my_idx'),
dtype = 'object',
name = 'my_name')
print(s)
显示结果如下:
对于其中的属性,我们可以通过.的方式来获取:
print(s.values)
print(s.index)
print(s.dtype)
print(s.name)
print(s.shape)
显示结果如下:
2.2.2 DataFrame
DataFrame
在Series
的基础上增加了列索引,一个数据框可以由二维的data与行列索引来构造:
data = [[1, 'a', 1.2], [2, 'b', 2.2], [3, 'c', 3.2]]
df = pd.DataFrame(data = data,
index = ['row_%d'%i for i in range(3)],
columns=['col_0', 'col_1', 'col_2'])
print(df)
显示结果如下:
但一般而言,我们更多的时候会采用从列索引名到数据的映射来构造数据框,同时再加上行索引:
df = pd.DataFrame(data = {'col_0': [1,2,3],
'col_1':list('abc'),
'col_2': [1.2, 2.2, 3.2]},
index = ['row_%d'%i for i in range(3)])
print(df)
显示结果如下:
由于这种映射关系,在DataFrame
中可以用[col_name]
与[col_list]
来取出相应的列与由多个列组成的表,结果分别为Series
和DataFrame
:
print(df['col_0'])
print(df[['col_0', 'col_1']])
显示结果如下:
与Series类似,在数据框中同样可以取出相应的属性。
2.3 常用基本函数
为了进行举例说明,在接下来的部分和其余章节都将会使用一份learn_pandas.csv
的虚拟数据集,它记录了四所学校学生的体测个人信息。
首先我们查看该数据中的列信息:
df = pd.read_csv('E:/pandas/data/learn_pandas.csv')
print(df.columns)
显示结果如下:
上述列名依次代表学校、年级、姓名、性别、身高、体重、是否为转系生、体测场次、测试时间、1000米成绩,在本章只需使用前七列。
通过df = df[df.columns[:7]]
命令提取前七列。
2.3.1 汇总函数
- head, tail函数分别表示返回表或者序列的前n行和后n行,其中n默认为5:
通过df.head(2)
提取前两行的信息,df.tail(3)
提取后三行的信息进行数据分析
显示结果为:
- info, describe分别返回表的信息概况和表中数值列对应的主要统计量:
显示结果为:
【NOTE】info
, describe
只能实现较少信息的展示,如果想要对一份数据集进行全面且有效的观察,特别是在列较多的情况下,推荐使用pandas-profiling
包.
2.3.2 特征统计函数
在Series
和DataFrame
上定义了许多统计函数,最常见的是sum
, mean
, median
, var
, std
, max
, min
。例如,选出身高和体重列进行演示:
df_demo = df[['Height', 'Weight']]
print(df_demo.mean())
print('-' * 50)
print(df_demo.max())
显示结果为:
此外,我们还需要介绍的是quantile
,count
,idxmax
这三个函数,它们分别返回的是分位数、非缺失值个数、最大值对应的索引:
print(df_demo.quantile(0.75))
print('-' * 50)
print(df_demo.count())
print('-' * 50)
print(df_demo.idxmax()) # idxmin是对应的函数
print('-' * 50)
显示结果为:
上面所有的函数,由于操作后返回的是标量,所以又称为聚合函数,它们有一个公共参数axis
,默认为0代表逐列聚合,如果设置为1则表示逐行聚合:
df_demo.mean(axis=1).head() # 在这个数据集上体重和身高的均值并没有意义
2.3.3 唯一值函数
对序列使用unique
和nunique
可以分别得到其唯一值组成的列表和唯一值的个数:
print(df['School'].unique())
print('-' * 50)
print(df['School'].nunique())
print('-' * 50)
显示结果为:
value_counts
可以得到唯一值和其对应出现的频数:
df['School'].value_counts()
显示结果为:
如果想要观察多个列组合的唯一值,可以使用drop_duplicates
。其中的关键参数是keep
,默认值first
表示每个组合保留第一次出现的所在行,last
表示保留最后一次出现的所在行,False
表示把所有重复组合所在的行剔除。
例:
df_demo = df[['Gender','Transfer','Name']]
df_demo.drop_duplicates(['Gender', 'Transfer'])
print(df_demo.drop_duplicates(['Gender', 'Transfer']))
显示结果为:
此外,duplicated
和drop_duplicates
的功能类似,但前者返回了是否为唯一值的布尔列表,其keep
参数与后者一致。其返回的序列,把重复元素设为True
,否则为False
。drop_duplicates
等价于把duplicated
为True
的对应行剔除
print(df_demo.duplicated(['Gender', 'Transfer']).head())
print('-' * 50)
显示结果为:
2.3.4 替换函数
一般而言,替换操作是针对某一个列进行的,因此下面的例子都以Series
举例。pandas中的替换函数可以归纳为三类:映射替换、逻辑替换、数值替换。其中映射替换包含replace
方法、第八章中的str.replace
方法以及第九章中的cat.codes
方法,此处介绍replace的用法。
在replace
中,我们可以通过字典构造,或者传入两个列表来进行替换:
print(df['Gender'].replace({'Female':0, 'Male':1}).head())
print('-' * 50)
print(['Gender'].replace(['Female', 'Male'], [0, 1]).head())
print('-' * 50)
另外,replace
还有一种特殊的方向替换,指定method
参数为ffill
则为用前面一个最近的未被替换的值进行替换,bfill
则使用后面最近的未被替换的值进行替换。从下面的例子可以看到,它们的结果是不同的:
s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
print(s.replace([1, 2], method='ffill'))
显示结果为:
print(s.replace([1, 2], method='bfill'))
注意:正则替换请使用str.replace
虽然对于replace
而言可以使用正则替换,但是当前版本下对于string类型的正则替换还存在bug,因此如有此需求,请选择str.replace
进行替换操作。
接下来我们将了逻辑替换,逻辑替换包括了where
和mask
,这两个函数是完全对称的:where
函数在传入条件为False
的对应行进行替换,而mask
在传入条件为True
的对应行进行替换,当不指定替换值时,替换为缺失值。
s = pd.Series([-1, 1.2345, 100, -50])
print(s.where(s<0))
print('-' * 50)
显示结果为:
需要注意的是,传入的条件只需是与被调用的Series索引一致的布尔序列即可:
s_condition= pd.Series([True,False,False,True],index=s.index)
print(s.mask(s_condition, -50))
print('-' * 50)
显示结果为:
数值替换包含了round, abs, clip方法,它们分别表示按照给定精度四舍五入、取绝对值和截断:
s = pd.Series([-1, 1.2345, 100, -50])
print(s.round(2))
print('-' * 50)
print(s.abs())
print('-' * 50)
print(s.clip(0, 2))# 前两个数分别表示上下截断边界
print('-' * 50)
显示结果为:
2.3.5 排序函数
排序共有两种方式,其一为值排序,其二为索引排序,对应的函数是sort_values和sort_index。
为了演示排序函数,下面先利用set_index方法把年级和姓名两列作为索引:
df_demo = df[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])
print(df_demo.head(3))
显示结果为:
紧接着我们对身高进行排序,默认参数ascending=True为升序:
print(df_demo.sort_values('Height').head())
print('-' * 50)
print(df_demo.sort_values('Height', ascending=False).head())
print('-' * 50)
显示结果为:
在排序中,经常遇到多列排序的问题,比如在体重相同的情况下,对身高进行排序,并且保持身高降序排列,体重升序排列:
df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
显示结果为:
索引排序的用法和值排序完全一致,只不过元素的值在索引中,此时需要指定索引层的名字或者层号,用参数level表示。另外,需要注意的是字符串的排列顺序由字母顺序决定。
2.3.6 apply方法
apply
方法常用于DataFrame
的行迭代或者列迭代,它的axis
含义与第2小节中的统计聚合函数一致,apply
的参数往往是一个以序列为输入的函数。例如对于.mean()
,使用apply
可以如下地写出:
df_demo = df[['Height', 'Weight']]
def my_mean(x):
res = x.mean()
return res
df_demo.apply(my_mean)
显示结果为:
同样的,可以利用lambda
表达式使得书写简洁,这里的x就指代被调用的df_demo
表中逐个输入的序列:
df_demo.apply(lambda x:x.mean())
显示结果为:
若指定axis=1,那么每次传入函数的就是行元素组成的Series,其结果与之前的逐行均值结果一致。
df_demo.apply(lambda x:x.mean(), axis=1).head()
显示结果为:
这里再举一个例子:mad
函数返回的是一个序列中偏离该序列均值的绝对值大小的均值,例如序列1,3,7,10中,均值为5.25,每一个元素偏离的绝对值为4.25,2.25,1.75,4.75,这个偏离序列的均值为3.25。现在利用apply
计算升高和体重的mad
指标:
print(df_demo.apply(lambda x:(x-x.mean()).abs().mean()))
显示结果为:
与使用内置的mad函数计算结果一致。
注意:谨慎使用apply
得益于传入自定义函数的处理,apply的自由度很高,但这是以性能为代价的。一般而言,使用pandas的内置函数处理和apply来处理同一个任务,其速度会相差较多,因此只有在确实存在自定义需求的情境下才考虑使用apply。
2.4 窗口对象
【WARNING】谨慎使用apply
得益于传入自定义函数的处理,apply的自由度很高,但这是以性能为代价的。一般而言,使用pandas的内置函数处理和apply来处理同一个任务,其速度会相差较多,因此只有在确实存在自定义需求的情境下才考虑使用apply。
【END】
四、窗口对象
pandas中有3类窗口,分别是滑动窗口rolling
、扩张窗口expanding
以及指数加权窗口ewm
。
2.4.1 滑窗对象
要使用滑窗函数,就必须先要对一个序列使用.rolling
得到滑窗对象,其最重要的参数为窗口大小window
。
s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)
roller
显示结果为:
在得到了滑窗对象后,能够使用相应的聚合函数进行计算,需要注意的是窗口包含当前行所在的元素,例如在第四个位置进行均值运算时,应当计算(2+3+4)/3,而不是(1+2+3)/3,此外,还支持使用apply传入自定义函数,其传入值是对应窗口的Series,例如上述的均值函数可以等效表示.
shift
, diff
, pct_change
是一组类滑窗函数,它们的公共参数为periods=n
,默认为1,分别表示取向前第n个元素的值、与向前第n个元素做差(与Numpy中不同,后者表示n阶差分)、与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作。
2.4.2 扩张窗口
扩张窗口又称累计窗口,可以理解为一个动态长度的窗口,其窗口的大小就是从序列开始处到具体操作的对应位置,其使用的聚合函数会作用于这些逐步扩张的窗口上。具体地说,设序列为a1, a2, a3, a4,则其每个位置对应的窗口即[a1]、[a1, a2]、[a1, a2, a3]、[a1, a2, a3, a4]。
s = pd.Series([1, 3, 6, 10])
s.expanding().mean()
显示结果为: