Martin
Pandas入门下 汇总和计算描述统计处理缺失数据层次化索引使用DataFrame的列其他有关pandas内容pandas对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从Series中提取单个值(如sum或mean)或从DataFrame的行或列中提取一个Series。跟对应的Numpy数组方法相比,它们都是基于没有确实数据的假设而构建的。来看一个例子:
>>> df = DataFrame([[1.4,np.nan],[7.1,-4.5],[np.nan,np.nan],[0.75,-1.3]],index=['a','b','c','d'],columns=['one','two']) >>> df one two a 1.40 NaN b 7.10 -4.5 c NaN NaN d 0.75 -1.3调用DataFrame的sum方法将返回以含有列小计的Series:
>>> df.sum() one 9.25 two -5.80 dtype: float64传入axis=1将会按行进行求和运算:
>>> df.sum(axis=1) a 1.40 b 2.60 c 0.00 d -0.55 dtype: float64NA值会被自动排除,除非整个切片(这里指的是行或列)都是NA。通过skipna选项可以禁用该功能:
>>> df.mean(axis=1,skipna=False) a NaN b 1.300 c NaN d -0.275 dtype: float64下表列出了这些简约方法的常用项:
选项说明axis约简的轴。DataFrame的行用0,列用1skipna排除缺失值,默认值Truelevel如果轴是层次化索引(MultiIndex),则根据level分组约简有些方法(如idxmax和idxmin)返回的是间接统计(比如达到最小值或最大值的索引):
>>> df.idxmax() one b two d dtype: object另一些方法则是累计型的:
>>> df.cumsum() # 逐行叠加 one two a 1.40 NaN b 8.50 -4.5 c NaN NaN d 9.25 -5.8还有种方法,它既不是约简型也不是累计型。describe就是这样的一个例子,它用于一次性产生多个汇总统计:
one two count 3.000000 2.000000 mean 3.083333 -2.900000 std 3.493685 2.262742 min 0.750000 -4.500000 25% NaN NaN 50% NaN NaN 75% NaN NaN max 7.100000 -1.300000对于非数值型数据,describe会产生另一种统计汇总:
>>> obj = Series(['a','a','b','c']*4) >>> obj 0 a 1 a 2 b 3 c 4 a 5 a 6 b 7 c 8 a 9 a 10 b 11 c 12 a 13 a 14 b 15 c dtype: object >>> obj.describe() count 16 unique 3 top a # 出现次数最高的选项 freq 8 # 针对的top的频率 dtype: object下表列出了所有与描述统计相关的方法:
方法说明count非NA值的数量describe针对Series或各DataFrame列计算汇总统计min、max计算最小值和最大值argmin、argmax计算能够获取到最小值和最大值的索引位置idxmin、idxmax计算能够获取到最小值和最大值的索引值quantile计算样本的分位数(0到1)sum值的总和mean值的平均数median值的算数中位数(50%分位数)mad根据平均值计算平均绝对离差var样本的方差额std样本的标准差skew样本值的偏度(三阶矩)kurt样本值的峰度(四阶矩)cumsum样本值的累计和cummin、cummax样本值的累计最大值和累计最小值cumpord样本值的累计积diff计算一阶差分pct_change计算百分数变化 唯一值、值计数以及成员资格还有一类方法可以从一维Series的值中抽取信息。来个例子:
>>> obj = Series(['c','a','d','a','a','b','b','c','c'])第一个函数是unique,它可以得到Series中的唯一值数组:
>>> uniques = obj.unique() >>> uniques array(['c', 'a', 'd', 'b'], dtype=object)返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序(uniques,sor())。value_counts用于计算一个Series中各值出现的频率:
>>> obj.value_counts() c 3 a 3 b 2 d 1 dtype: int64为了便于查看,结果Series是按降序排列的(由参数ascending=False控制)。value_counts还是一个顶级ndarray的方法,可用于任何数组或序列:
>>> obj.value_counts(ascending=True) d 1 b 2 a 3 c 3 dtype: int64最后是isin函数,它用于判断矢量化集合的成员资格,可用于选取Series中或DataFrame列中数据的子集:
>>> mask = obj.isin(['c','b']) >>> mask 0 True 1 False 2 False 3 False 4 False 5 True 6 True 7 True 8 True dtype: bool >>> obj[mask] # 可通过返回的布尔索引进行数据抽取 0 c 5 b 6 b 7 c 8 c dtype: object下表给出了这几个方法的一些参考信息:
方法说明isin计算一个表示Series各值是否包含于传入的值序列中的布尔型数组unique计算Series中的唯一数组,按发现的顺序返回(只适用于Series)value_counts返回一个Series,其索引为唯一值,其值为频率,按计数值将序排列缺失数据(missing data)在大部分数据分析应用中都常见。Pandas的设计目标之一就是让缺失数据的处理任务尽量轻松。例如,Pandas对象上的所有描述统计都排除了缺失数据,正如上节所讲的内容。 Pandas使用浮点值NaN表示浮点和非浮点数组中的缺失数据。它只是一个便于被检测出来的标记而已。
Python内置的None值也会被当成NA处理:
>>> data = Series(['a','b',np.nan,'f']) >>> data 0 a 1 b 2 NaN 3 f dtype: object >>> data[0] = None # 改值并赋值为空 >>> data 0 None 1 b 2 NaN 3 f dtype: object >>> data.isnull() # 判断是否为NA值 0 True 1 False 2 True 3 False dtype: bool下表给出NA的处理方法:
方法说明dropna根据各标签的值中是否缺失来对轴标签进行过滤,可通过阈值调节对缺失值的容忍度fillna用指定值或插值方法(ffill或bfill)填充缺失数据isnull返回一个含有布尔值的对象,这些布尔值表示哪些值是缺失的notnullisnull的否定形式 滤除缺失数据过滤掉缺失数据的办法有很多种,纯手工操作永远都是一个办法,但是dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series:(不影响 源数据)
>>> from numpy import nan as NA >>> data = Series([1,NA,3.5,NA,7]) >>> data.dropna() 0 1.0 2 3.5 4 7.0 dtype: float64当然,也可以通过布尔型索引达到这个目的:
>>> data[data.notnull()] 0 1.0 2 3.5 4 7.0 dtype: float64而对于DataFrame对象事情就变得有点复杂了,你可能希望丢弃全NA或含有NA的行或列。dropna默认丢弃任何含有缺失值的行(即参数默认how=any):
>>> data = DataFrame([[1,6.5,3],[1,NA,NA],[NA,NA,NA],[NA,6.5,3]]) >>> data 0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0 >>> cleaned = data.dropna() >>> cleaned 0 1 2 0 1.0 6.5 3.0传入how=’all’将只丢弃全为NA的那些行:
>>> data.dropna(how='all') 0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 3 NaN 6.5 3.0要用这中方式丢弃列只需传入axis=1参数即可:
>>> data[4] = NA >>> data 0 1 2 4 0 1.0 6.5 2.0 NaN 1 1.0 NaN NaN NaN 2 NaN NaN NaN NaN 3 NaN 6.5 3.0 NaN >>> data.dropna(how='all',axis=1) 0 1 2 0 1.0 6.5 2.0 1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0 填充缺失数据如果你不想过滤缺失数据,而是希望通过其他方式填补那些‘空洞’。对于大多数情况而言,fillna方法是最主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值:
>>> data.fillna(0) 0 1 2 4 0 1.0 6.5 2.0 0.0 1 1.0 0.0 0.0 0.0 2 0.0 0.0 0.0 0.0 3 0.0 6.5 3.0 0.0若是通过一个字典调用fillna,就可以实现对不同的列填充不同的值:
>>> data.fillna({2:0.5}) 0 1 2 4 0 1.0 6.5 2.0 NaN 1 1.0 NaN 0.5 NaN 2 NaN NaN 0.5 NaN 3 NaN 6.5 3.0 NaN注意:如果尝试‘data.fillna({2:0.5},axis=1)’则会报错 ‘NotImplementedError: Currently only can fill with dict/Series column by column’意思是只能在对列填充赋值,换句话说就是:目前只能在行方向上进行填充赋值。 下表列出了fillna函数的参数
参数说明value用于填充缺失值的标量值或字典对象method插值方式。如果函数调用时未指定其他参数的话,默认为ffillaxis待填充的轴,默认为axis=0inplace修改调用者对象而不产生副本limit可以连续填充的最大数量层次化索引(hierarchical indexing)是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别。抽象点说,它使你能以低维度形式处理高纬度数据。先来个例子:创建一个Series,并用一个由列表或数组组成的列表作为索引。
>>> data = Series(np.random.randn(10),index=[['a','a','a','b','b','b','c','c','d','d'],[1,2,3,1,2,3,1,2,2,3]]) >>> data a 1 -1.642208 2 0.502649 3 0.135720 b 1 0.081373 2 -0.763712 3 0.064475 c 1 -0.260353 2 0.669885 d 2 -0.477210 3 -0.127682 dtype: float64这就是带有MultiIndex索引的Series的格式化输出形式。索引之间的“间隔”表示“直接使用上面的标签”:
>>> data.index MultiIndex(levels=[[u'a', u'b', u'c', u'd'], [1, 2, 3]], labels=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2]])对于一个层次化索引对象,选取数据子集的操作很简单:
>>> data['b'] 1 0.081373 2 -0.763712 3 0.064475 dtype: float64 >>> data['b':'c'] # 标签切片 b 1 0.081373 2 -0.763712 3 0.064475 c 1 -0.260353 2 0.669885 dtype: float64 >>> data.ix[['b','d']] # 多个索引标签 b 1 0.081373 2 -0.763712 3 0.064475 d 2 -0.477210 3 -0.127682 dtype: float64有时甚至可以在‘内层’中进行选取:
>>> data[:,2] # 仍为标签索引 a 0.502649 b -0.763712 c 0.669885 d -0.477210 dtype: float64层次化索引在数据重塑和基于分组的操作(如透视表的生成)中扮演者重要的角色。比如这段数据可以通过unstack方法被重新安排到一个DataFrame中:
>>> data.unstack() 1 2 3 a -1.642208 0.502649 0.135720 b 0.081373 -0.763712 0.064475 c -0.260353 0.669885 NaN d NaN -0.477210 -0.127682 >>> type(data.unstack()) <class 'pandas.core.frame.DataFrame'> # 将DataFrame类型unstack的逆运算是stack:
>>> test = data.unstack() >>> test 1 2 3 a -1.642208 0.502649 0.135720 b 0.081373 -0.763712 0.064475 c -0.260353 0.669885 NaN d NaN -0.477210 -0.127682 >>> test.stack() a 1 -1.642208 2 0.502649 3 0.135720 b 1 0.081373 2 -0.763712 3 0.064475 c 1 -0.260353 2 0.669885 d 2 -0.477210 3 -0.127682 dtype: float64 >>> type(test.stack()) <class 'pandas.core.series.Series'> # 又转化为Series类型stack和unstack将在以后详细讲解。
对于一个DataFrame,每条轴都可以有分层索引:
>>> frame = DataFrame(np.arange(12).reshape((4,3)),index=[['a','a','b','b'],[1,2,1,2]],columns=[['Ohio','Ohio','Colorado'],['Green','Red','Green']]) >>> frame Ohio Colorado Green Red Green a 1 0 1 2 2 3 4 5 b 1 6 7 8 2 9 10 11各层都可以有名字(可以使字符串,也可以是别的Python对象).如果指定了名称,它们就会显示在控制台输出中:
>>> frame.index.names = ['key1','key2'] >>> frame.columns.names = ['state','color'] >>> frame state Ohio Colorado color Green Red Green key1 key2 a 1 0 1 2 2 3 4 5 b 1 6 7 8 2 9 10 11由于有了分部的列索引,因此可以轻松的选取列分组:
>>> frame['Ohio'] # 通过一级索引选取整个数据 color Green Red key1 key2 a 1 0 1 2 3 4 b 1 6 7 2 9 10 重排分级顺序 有时,你需要重新调整某条轴上各级别的顺序,或根据指定级别上的值对数据进行排序。swaplevel接受两个级别编号或名称,并返回一个互换了级别的新对象(但数据不会发生变化): >>> frame.swaplevel('key1','key2') state Ohio Colorado color Green Red Green key2 key1 1 a 0 1 2 2 a 3 4 5 1 b 6 7 8 2 b 9 10 11而sortlevel则根据单个级别中的值对数据进行排序(稳定的)。交换级别时,常常也会用到sortlevel,这样最终结果就是有序的了:
>>> frame.sortlevel(1) # 查看与原数据有什么不同 state Ohio Colorado color Green Red Green key1 key2 a 1 0 1 2 b 1 6 7 8 a 2 3 4 5 b 2 9 10 11 >>> frame.swaplevel(0,1).sortlevel(0) # 根据索引进行重排,并根据0号索引排序 state Ohio Colorado # 进行了一系列操作可以看出与原数据的不同 color Green Red Green # 可与下面的frame进行比较 key2 key1 1 a 0 1 2 b 6 7 8 2 a 3 4 5 b 9 10 11 >>> frame state Ohio Colorado color Green Red Green key1 key2 a 1 0 1 2 2 3 4 5 b 1 6 7 8 2 9 10 11 根据级别汇总统计许多对DataFrame和Series的描述和汇总统计都有一个level选项,它用于指定在某条轴上求和的级别。再以上面那个DataFrame为例,我们可以根据行或列上的级别来进行求和,如下所示:
>>> frame state Ohio Colorado color Green Red Green key1 key2 a 1 0 1 2 2 3 4 5 b 1 6 7 8 2 9 10 11 >>> frame.sum(level='key2') state Ohio Colorado color Green Red Green key2 1 6 8 10 2 12 14 16 >>> frame.sum(level=1) state Ohio Colorado color Green Red Green key2 1 6 8 10 2 12 14 16这其实是利用了pandas的groupby功能,此功能以后会详细讲解。
人们经常想要将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame的列,来个例子先:
>>> frame = DataFrame({'a':range(7),'b':range(7,0,-1),'c':['one','one','one','two','two','two','two'],'d':[0,1,2,0,1,2,3]}) >>> frame a b c d 0 0 7 one 0 1 1 6 one 1 2 2 5 one 2 3 3 4 two 0 4 4 3 two 1 5 5 2 two 2 6 6 1 two 3DataFrame的set_index函数会将其一个或多个列转化为行索引,并创建一个新的DataFrame:
>>> frame2 = frame.set_index(['c','d']) >>> frame2 a b # 注意改变后的效果 c d one 0 0 7 1 1 6 2 2 5 two 0 3 4 1 4 3 2 5 2 3 6 1默认情况下,那些列会从DataFrame中移除,但也可以将其保留下来(通过参数drop=False控制):
>>> frame.set_index(['c','d'],drop=False) a b c d c d one 0 0 7 one 0 1 1 6 one 1 2 2 5 one 2 two 0 3 4 two 0 1 4 3 two 1 2 5 2 two 2 3 6 1 two 3reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:
>>> frame2.reset_index() c d a b 0 one 0 0 7 1 one 1 1 6 2 one 2 2 5 3 two 0 3 4 4 two 1 4 3 5 two 2 5 2 6 two 3 6 1操作由整数索引的pandas对象有时很麻烦,因为会有歧义:
>>> ser = Series(np.arange(3)) >>> ser 0 0 1 1 2 2 dtype: int64 >>> ser[-1] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/martin/anaconda2/lib/python2.7/site-packages/pandas/core/series.py", line 583, in __getitem__ result = self.index.get_value(self, key)这样的取值方式会产生歧义,因为pandas对象不知道你是要按普通索引(0,1,2…)来取值,还是要按标签索引来取值。但是如果索引是非整数就不会产生歧义:
>>> ser = Series(np.arange(3),index=['a','b','c']) >>> ser a 0 b 1 c 2 dtype: int64 >>> ser[-1] 2如果你需要可靠的、不考虑索引类型的、基于位置的索引,可以使用Series的iloc[]方法和DataFrame的irow()和icol()方法。
>>> ser2 = Series(range(3),index=[-5,1,3]) >>> ser2 -5 0 1 1 3 2 dtype: int64 >>> ser2.iloc[2] 2 >>> frame = DataFrame(np.arange(6).reshape(3,2),index=[2,0,1]) >>> frame 0 1 2 0 1 0 2 3 1 4 5 >>> frame.irow(2) 0 4 1 5 Name: 1, dtype: int64