02 pandas入门

目录

1. pandas数据结构介绍

1.1 Series

1.2 DataFrame

1.3 索引对象

2. 基本功能

2.1 重建索引

2.2 轴向上删除条目

2.3 索引、选择与过滤

2.3.1 使用loc和iloc选择数据

2.4 整数索引

2.5 算术和数据对齐

2.5.1 使用填充值的算术方法

2.5.2 DataFrame和Series间的操作

2.6 函数应用和映射

2.7 排序和排名

2.8 含有重复标签的轴索引

3. 描述性统计的概述与计算

3.1 相关性和协方差

3.2 唯一值、计数和成员属性


pandas所包含的数据结构和数据处理工具的设计使得在Python中进行数据清洗和分析非常快捷。pandas经常是和其他数值计算工具,比如NumPy和SciPy,以及数据可视化工具比如matplotlib一起使用的。pandas支持大部分NumPy语言风格的数组计算,尤其是数组函数以及没有for循环的各种数据处理。

pandas是用来处理表格型或异质型数据的。而NumPy则相反,它更适合处理同质型的数值类数组数据。

# 导入pandas
import pandas as pd

还可以方便地从本地命名空间中导入Series和DataFrame,它们是常用的类

from pandas import Series, DataFrame

1. pandas数据结构介绍

为了入门pandas,需要熟悉两个常用的工具数据结构:Series和DataFrame。尽管他们并不能解决所有的问题,但它们为大多数应用提供了一个有效、易用的基础。

1.1 Series

Series是一种一维的数组型对象,它包含了一个值序列(与NumPy中的类型相似),并且包含了数据标签,称为索引(index)。最简单的序列可以仅仅由一个数组形成:

>>> obj = pd.Series([4, 7, -5, 3])
>>> obj
0    4
1    7
2   -5
3    3
dtype: int64

交互式环境中Series的字符串表示,索引在左边,值在右边。由于我们不为数据指定索引,默认生成的索引是从0到N-1(N是数据的长度)。可以通过values属性和index属性分别获得Series对象的值和索引:

>>> obj.values
array([ 4,  7, -5,  3])

>>> obj.index  # 与 range(4)类似
RangeIndex(start=0, stop=4, step=1)

通常需要创建一个索引序列,用标签标识每个数据点:

>>> obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
>>> obj2
d    4
b    7
a   -5
c    3
dtype: int64

>>> obj2.index
Index(['d', 'b', 'a', 'c'], dtype='object')

与NumPy的数组相比,你可以在从数据中选择数据的时候使用标签来进行索引:

>>> obj2['a']
-5
>>> obj2['d'] = 6

>>> obj2[['c', 'a', 'd']]
c    3
a   -5
d    6
dtype: int64

上面的例子中,['c','a','d']包含的不是数字而是字符串,作为索引列表。

使用NumPy的函数或NumPy风格的操作,比如使用布尔值数组进行过滤,与标量相乘,或是应用数学函数,这些操作将保存索引值连接:

>>> obj2[obj2 > 0]
d    6
b    7
c    3
dtype: int64

>>> obj2 * 2
d    12
b    14
a   -10
c     6
dtype: int64

>>> np.exp(obj2)
d     403.428793
b    1096.633158
a       0.006738
c      20.085537
dtype: float64

从另一个角度考虑Series,可以认为它是一个长度固定且有序的字典,因为它将索引值和数据值按位置配对。在你可能会使用字典的上下文中,也可以使用Series:

>>> 'b' in obj2
True

>>> 'e' in obj2
False

如果你已经有数据包含在Python字典中,你可以使用字典生成一个Series:

>>> sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
>>> obj3 = pd.Series(sdata)
>>> obj3
Ohio      35000
Oregon    16000
Texas     71000
Utah       5000
dtype: int64

当你把字典传递给Series构造函数时,产生的Series的索引将是排序好的字典键。你可以将字典键按照你所想要的顺序传递给构造函数,从而使生成的Series的索引顺序符合你的预期:

>>> states = ['California', 'Ohio', 'Oregon', 'Texas']
>>> obj4 = pd.Series(sdata, index=states)
>>> obj4
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64

上面的例子中,sdata中的三个值被放置在正确的位置,但是因为'California'没有出现在sdata的键中,它对应的值是NaN(not a number),这是pandas中标记缺失值或NA值的方式。因为'Utah'并不在states中,它被排除在结果对象外。

pandas中使用isnull和notnull函数来检查缺失数据:

>>> pd.isnull(obj4)
California     True
Ohio          False
Oregon        False
Texas         False
dtype: bool

>>> pd.notnull(obj4)
California    False
Ohio           True
Oregon         True
Texas          True
dtype: bool

isnull和notnull也是Series的实例方法:

>>> obj4.isnull()
California     True
Ohio          False
Oregon        False
Texas         False
dtype: bool

对于很多应用来说,在数学操作中自动对齐索引是Series的一个非常有用的特性:

>>> obj3
Ohio      35000
Oregon    16000
Texas     71000
Utah       5000
dtype: int64

>>> obj4
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64

>>> obj3 + obj4
California         NaN
Ohio           70000.0
Oregon         32000.0
Texas         142000.0
Utah               NaN
dtype: float64

Series对象自身和其索引都有name属性,这个特性与pandas其他重要功能集成在一起:

>>> obj4.name = 'population'
>>> obj4.index.name = 'state'
>>> obj4
state
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
Name: population, dtype: float64

Series的索引可以通过按位置赋值的方式进行改变:

>>> obj
0    4
1    7
2   -5
3    3
dtype: int64

>>> obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']

>>> obj
Bob      4
Steve    7
Jeff    -5
Ryan     3
dtype: int64

1.2 DataFrame

DataFrame表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被视为一个共享相同索引的Series的字典。在DataFrame中,数据被存储为一个以上的二维块,而不是列表、字典或其他一维数组的集合。

尽管DataFrame是二维的,但你可以利用分层索引在DataFrame中展现更高维度的数据。分层索引是pandas中一种更为高级的数据处理特性。

有多种方式可以构建DataFrame,其中最常用的方式是利用包含等长度列表或NumPy数组的字典来形成DataFrame:

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002, 2003],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)

产生的DataFrame会自动为Sereies分配索引,并且列会按照排序的顺序排列:

>>> frame
	state	year	pop
0	Ohio	2000	1.5
1	Ohio	2001	1.7
2	Ohio	2002	3.6
3	Nevada	2001	2.4
4	Nevada	2002	2.9
5	Nevada	2003	3.2

对于大型DataFrame,head方法将会只选出头部的五行:

>>> frame.head()
	state	year	pop
0	Ohio	2000	1.5
1	Ohio	2001	1.7
2	Ohio	2002	3.6
3	Nevada	2001	2.4
4	Nevada	2002	2.9

 如果你指定了列的顺序,DataFrame的列将会按照指定顺序排列:

>>> pd.DataFrame(data, columns=['year', 'state', 'pop'])
   year   state  pop
0  2000    Ohio  1.5
1  2001    Ohio  1.7
2  2002    Ohio  3.6
3  2001  Nevada  2.4
4  2002  Nevada  2.9
5  2003  Nevada  3.2

如果你传的列不包含在字典中,将会在结果中出现缺失值:

>>> frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
>>>                       index=['one', 'two', 'three', 'four', 'five', 'six'])
>>>frame2
       year   state  pop debt
one    2000    Ohio  1.5  NaN
two    2001    Ohio  1.7  NaN
three  2002    Ohio  3.6  NaN
four   2001  Nevada  2.4  NaN
five   2002  Nevada  2.9  NaN
six    2003  Nevada  3.2  NaN

>>> frame2.columns
Index(['year', 'state', 'pop', 'debt'], dtype='object')

DataFrame中的一列,可以按字典型标记或属性那样检索为Series:

>>> frame2['state']
one        Ohio
two        Ohio
three      Ohio
four     Nevada
five     Nevada
six      Nevada
Name: state, dtype: object

>>> frame2.year
one      2000
two      2001
three    2002
four     2001
five     2002
six      2003
Name: year, dtype: int64

在IPython中,属性型连接(比如frame2.year)和列名的tab补全是非常方便的。

frame2[colunm]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。

请注意,返回的Series与原DataFrame有相同的索引,且Series的name属性也会被合理地设置。

行也可以通过位置或特殊属性loc进行选取

>>> frame2.loc['three']
year     2002
state    Ohio
pop       3.6
debt      NaN
Name: three, dtype: object

列的引用是可以修改的。例如,空的'debt'列可以赋值为标量值或值数组:

>>> frame2['debt'] = 16.5
>>> frame2
       year   state  pop  debt
one    2000    Ohio  1.5  16.5
two    2001    Ohio  1.7  16.5
three  2002    Ohio  3.6  16.5
four   2001  Nevada  2.4  16.5
five   2002  Nevada  2.9  16.5
six    2003  Nevada  3.2  16.5

>>> frame2['debt'] = np.arange(6.)
>>> frame2
       year   state  pop  debt
one    2000    Ohio  1.5   0.0
two    2001    Ohio  1.7   1.0
three  2002    Ohio  3.6   2.0
four   2001  Nevada  2.4   3.0
five   2002  Nevada  2.9   4.0
six    2003  Nevada  3.2   5.0

当你将列表或数组赋值给一个列时,值的长度必须和DataFrame的长度相匹配。如果你将Series赋值给一列时,Series的索引将会按照DataFrame的索引重新排列,并在空缺的地方填充缺失值:

>>> val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
>>> frame2['debt'] = val
>>> frame2
       year   state  pop  debt
one    2000    Ohio  1.5   NaN
two    2001    Ohio  1.7  -1.2
three  2002    Ohio  3.6   NaN
four   2001  Nevada  2.4  -1.5
five   2002  Nevada  2.9  -1.7
six    2003  Nevada  3.2   NaN

如果被赋值的列并不存在,则会生成一个新的列。del关键字可以像在字典中那样对DataFrame删除列。

在del的例子中,我首先增加一列,这一列是布尔值,判断条件是state列是否为'ohio':

>>> frame2['eastern'] = frame2.state == 'Ohio'
>>> frame2
       year   state  pop  debt  eastern
one    2000    Ohio  1.5   NaN     True
two    2001    Ohio  1.7  -1.2     True
three  2002    Ohio  3.6   NaN     True
four   2001  Nevada  2.4  -1.5    False
five   2002  Nevada  2.9  -1.7    False
six    2003  Nevada  3.2   NaN    False

frame2.eastern的语法无法创建新的列。

del方法可以用于移除之前新建的列:

>>> del frame2['eastern']
>>> frame2.columns
Index(['year', 'state', 'pop', 'debt'], dtype='object')

从DataFrame中选取的列是数据的视图,而不是拷贝。因此,对Series的修改会映射到DataFrame中。如果需要复制,则应当显式地使用Series的copy方法。

另一种常用的数据形式是包含字典的嵌套字典

>>> pop = {'Nevada': {2001: 2.4, 2002: 2.9},
>>>        'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}

如果嵌套字典被赋值给DataFrame,pandas会将字典的键作为列,将内部字典的键作为行索引:

>>> frame3 = pd.DataFrame(pop)
>>> frame3
      Nevada  Ohio
2000     NaN   1.5
2001     2.4   1.7
2002     2.9   3.6

你可以将使用类似NumPy的语法对DataFrame进行转置操作(调换行和列):

>>> frame3.T
        2000  2001  2002
Nevada   NaN   2.4   2.9
Ohio     1.5   1.7   3.6

内部字典的键被联合、排序后形成了结果的索引。如果已经显式指明索引的话,内部字典的键将不会被排序:

>>> pd.DataFrame(pop, index=[2001, 2002, 2003])
      Nevada  Ohio
2001     2.4   1.7
2002     2.9   3.6
2003     NaN   NaN

包含Series的字典也可以用于构造DataFrame:

>>> pdata = {'Ohio': frame3['Ohio'][:-1],
>>>          'Nevada': frame3['Nevada'][:2]}
>>> pd.DataFrame(pdata)
      Nevada  Ohio
2000     NaN   1.5
2001     2.4   1.7

如果DataFrame的索引和列拥有name属性,则这些name属性也会被显示:

>>> frame3.index.name = 'year'; frame3.columns.name = 'state'
>>> frame3 
state  Nevada  Ohio
year
2000      NaN   1.5
2001      2.4   1.7
2002      2.9   3.6

和Series类似,DataFrame的values属性会将包含在DataFrame中的数据以二维ndarray的形式返回:

>>> frame3.values
array([[ nan,  1.5],
       [ 2.4,  1.7],
       [ 2.9,  3.6]])

如果DataFrame的列是不同的dtypes,则values的dtype会自动选择适合所有列的类型:

>>> frame2.values
array([[2000, 'Ohio', 1.5, nan],
       [2001, 'Ohio', 1.7, -1.2],
       [2002, 'Ohio', 3.6, nan],
       [2001, 'Nevada', 2.4, -1.5],
       [2002, 'Nevada', 2.9, -1.7],
       [2003, 'Nevada', 3.2, nan]], dtype=object)

DataFrame构造函数的有效输入

1.3 索引对象

pandas中的索引对象是用于存储轴标签和其他元数据的(例如轴名称或标签)。在构造Series或DataFrame时,你所使用的任意数组或标签序列都可以在内部转换为索引对象:

>>> obj = pd.Series(range(3), index=['a', 'b', 'c'])
>>> index = obj.index
>>> index
Index(['a', 'b', 'c'], dtype='object')

>>> index[1:]
Index(['b', 'c'], dtype='object')

索引对象是不可变的,因此用户是无法修改索引对象的:

>>> index[1] = 'd'  # TypeError

不变性使得在多种数据结构中分享索引对象更为安全:

>>> labels = pd.Index(np.arange(3))
>>> labels
Int64Index([0, 1, 2], dtype='int64')

>>> obj2 = pd.Series([1.5, -2.5, 0], index=labels)
>>> obj2
0    1.5
1   -2.5
2    0.0
dtype: float64

>>> obj2.index is labels
True

一些用户并不经常利用索引对象提供的功能,但是因为一些操作会产生包含索引化数据的结果,理解索引如何工作还是很重要的。

除了类似数组,索引对象也像一个固定大小的集合:

>>> frame3
state  Nevada  Ohio
year
2000      NaN   1.5
2001      2.4   1.7
2002      2.9   3.6

>>> frame3.columns
Index(['Nevada', 'Ohio'], dtype='object', name='state')

>>> 'Ohio' in frame3.columns
True

>>> 2003 in frame3.index
False

与Python集合不同,pandas索引对象可以包含重复标签:

>>> dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
>>> dup_labels
Index(['foo', 'foo', 'bar', 'bar'], dtype='object')

根据重复标签进行筛选,会选取所有重复标签对应的数据。

每个索引都有一些集合逻辑的方法和属性,这些方法和属性解决了关于它所包含的数据的其他常见问题。

一些索引对象的方法和属性

2. 基本功能

2.1 重建索引

reindex是pandas对象的重要方法,该方法用于创建一个符合新索引的新对象。

>>> obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
>>> obj
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64

Series调用reindex方法时,会将数据按照新的索引进行排列,如果某个索引值之前并不存在,则会引入缺失值:

>>> obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
>>> obj2
a   -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64

对于顺序数据,比如时间序列,在重建索引时可能会需要进行插值或填值。method可选参数允许我们使用诸如ffill等方法在重建索引时插值,ffill方法会将值前向填充:

>>> obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
>>> obj3
0      blue
2    purple
4    yellow
dtype: object

>>> obj3.reindex(range(6), method='ffill')
0      blue
1      blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object

在DataFrame中,reindex可以改变行索引、列索引,也可以同时改变二者。当仅传入一个序列时,结果中的行会重建索引:

>>> frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
>>>                      index=['a', 'c', 'd'],
>>>                      columns=['Ohio', 'Texas', 'California'])

>>> frame
   Ohio  Texas  California
a     0      1           2
c     3      4           5
d     6      7           8

>>> frame2 = frame.reindex(['a', 'b', 'c', 'd'])
>>> frame2
   Ohio  Texas  California
a   0.0    1.0         2.0
b   NaN    NaN         NaN
c   3.0    4.0         5.0
d   6.0    7.0         8.0

列可以使用columns关键字重建索引:

>>> states = ['Texas', 'Utah', 'California']
>>> frame.reindex(columns=states)
   Texas  Utah  California
a      1   NaN           2
c      4   NaN           5
d      7   NaN           8

使用loc进行更为简洁的标签索引,许多用户更倾向于使用这种方式:

>>> frame.loc[['a', 'b', 'c', 'd'], states]
   Texas  Utah  California
a    1.0   NaN         2.0
b    NaN   NaN         NaN
c    4.0   NaN         5.0
d    7.0   NaN         8.0

reindex方法的参数

2.2 轴向上删除条目

drop方法会返回一个含有指示值或轴向上删除值的新对象:

>>> obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
>>> obj
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64

>>> new_obj = obj.drop('c')
>>> new_obj
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64

>>> obj.drop(['d', 'c'])
a    0.0
b    1.0
e    4.0
dtype: float64

在DataFrame中,索引值可以从轴向上删除。

>>> data = pd.DataFrame(np.arange(16).reshape((4, 4)),
>>>            index=['Ohio', 'Colorado', 'Utah', 'New York'],
>>>            columns=['one', 'two', 'three', 'four'])

>>> data
          one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

在调用drop时使用标签序列会根据行标签删除值(轴0):

>>> data.drop(['Colorado', 'Ohio'])
          one  two  three  four
Utah        8    9     10    11
New York   12   13     14    15

可以通过传递axis=1或axis='columns'来从列中删除值:

>>> data.drop('two', axis=1)
          one  three  four
Ohio        0      2     3
Colorado    4      6     7
Utah        8     10    11
New York   12     14    15

>>> data.drop(['two', 'four'], axis='columns')
          one  three
Ohio        0      2
Colorado    4      6
Utah        8     10
New York   12     14

很多函数,例如drop,会修改Series或DataFrame的尺寸或形状,这些方法直接操作原对象而不返回新对象:

# inplace属性,它会清除被删除的数据
>>> obj.drop('c', inplace=True)
>>> obj
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64

2.3 索引、选择与过滤

Series的索引(obj[...])与NumPy数组索引的功能类似,只不过Series的索引值可以不仅仅是整数。

>>> obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
>>> obj
a    0.0
b    1.0
c    2.0
d    3.0
dtype: float64

>>> obj['b']
1.0

>>> obj[1]
1.0

>>> obj[2:4]
c    2.0
d    3.0
dtype: float64

>>> obj[['b', 'a', 'd']]
b    1.0
a    0.0
d    3.0
dtype: float64

>>> obj[[1, 3]]
b    1.0
d    3.0
dtype: float64

>>> obj[obj < 2]
a    0.0
b    1.0
dtype: float64

普通的Python切片中是不包含尾部的,Series的切片与之不同:

>>>  obj['b':'c']
b    1.0
c    2.0
dtype: float64

使用这些方法设值时会修改Series相应的部分:

>>> obj['b':'c'] = 5
>>> obj
a    0.0
b    5.0
c    5.0
d    3.0
dtype: float64

使用单个值或序列,可以从DataFrame中索引出一个或多个列:

>>> data = pd.DataFrame(np.arange(16).reshape((4, 4)),
>>>            index=['Ohio', 'Colorado', 'Utah', 'New York'],
>>>            columns=['one', 'two', 'three', 'four'])

>>> data
          one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

>>> data['two']
Ohio         1
Colorado     5
Utah         9
New York    13
Name: two, dtype: int64

>>> data[['three', 'one']]
          three  one
Ohio          2    0
Colorado      6    4
Utah         10    8
New York     14   12

这种索引方式也有特殊案例。首先,可以根据一个布尔值数组切片或选择数据:

>>> data[:2]
          one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7

>>> data[data['three'] > 5]
          one  two  three  four
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

行选择语法data[:2]非常方便。传递单个元素或一个列表到[]符号中可以选择列。

另一个用例是使用布尔值DataFrame进行索引,布尔值DataFrame可以是对标量值进行比较产生的:

>>> data < 5
            one    two  three   four
Ohio       True   True   True   True
Colorado   True  False  False  False
Utah      False  False  False  False
New York  False  False  False  False

>>> data[data < 5] = 0
>>> data
          one  two  three  four
Ohio        0    0      0     0
Colorado    0    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

2.3.1 使用loc和iloc选择数据

轴标签(loc)、整数标签(iloc)

通过标签选出单行多列的数据

>>> data.loc['Colorado', ['two', 'three']]
two      5
three    6
Name: Colorado, dtype: int64

使用整数标签iloc进行类似的数据选择

>>> data.iloc[2, [3, 0, 1]]
four    11
one      8
two      9
Name: Utah, dtype: int64

>>> data.iloc[2]
one       8
two       9
three    10
four     11
Name: Utah, dtype: int64

>>> data.iloc[[1, 2], [3, 0, 1]]
          four  one  two
Colorado     7    0    5
Utah        11    8    9

索引功能还可以用于切片

>>> data.loc[:'Utah', 'two']
Ohio        0
Colorado    5
Utah        9
Name: two, dtype: int64

>>> data.iloc[:, :3][data.three > 5]
          one  two  three
Colorado    0    5      6
Utah        8    9     10
New York   12   13     14

DataFrame索引选项

2.4 整数索引

在pandas对象使上用整数索引对新用户来说经常会产生歧义,这是因为它和在列表、元组等Python内建数据结构上进行索引有些许不同。例如,你可能认为下面的代码会产生错误:

>>> ser = pd.Series(np.arange(3.))
>>> ser
# 会报错
>>> ser[-1]

在上面的例子中,pandas可以“回退”到整数索引,但是这样的方式难免会引起一些微小的错误。假设我们有一个索引,它包含了0、1、2,但是推断用户所需要的索引方式(标签索引或位置索引)是很难的:

>>> ser
0    0.0
1    1.0
2    2.0
dtype: float64

另一方面,对于非整数索引,则不会有潜在的歧义:

>>> ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
>>> ser2[-1]
2.0

为了保持一致性,如果你有一个包含整数的轴索引,数据选择时请始终使用标签索引。为了更精确地处理,可以使用loc(用于标签)或iloc(用于整数):

>>> ser[:1]
0    0.0
dtype: float64

>>> ser.loc[:1]
0    0.0
1    1.0
dtype: float64

>>> ser.iloc[:1]
0    0.0
dtype: float64

2.5 算术和数据对齐

不同索引的对象之间的算术行为是pandas提供给一些应用的一项重要特性。当你将对象相加时,如果存在某个索引对不相同,则返回结果的索引将是索引对的并集。对数据库用户来说,这个特性类似于索引标签的自动外连接(outer join)。

>>> s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
>>> s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
>>>                index=['a', 'c', 'e', 'f', 'g'])

>>> s1
a    7.3
c   -2.5
d    3.4
e    1.5
dtype: float64

>>> s2
a   -2.1
c    3.6
e   -1.5
f    4.0
g    3.1
dtype: float64

# 相加
>>> s1 + s2
a    5.2
c    1.1
d    NaN
e    0.0
f    NaN
g    NaN
dtype: float64

没有交叠的标签位置上,内部数据对齐会产生缺失值。缺失值会在后续的算术操作上产生影响。在DataFrame的示例中,行和列上都会执行对齐:

>>> df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
>>>                 index=['Ohio', 'Texas', 'Colorado'])

>>> df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
>>>                index=['Utah', 'Ohio', 'Texas', 'Oregon'])
>>> df1
            b    c    d
Ohio      0.0  1.0  2.0
Texas     3.0  4.0  5.0
Colorado  6.0  7.0  8.0
>>> df2
          b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0

将这些对象加在一起,返回一个DataFrame,它的索引、列是每个DataFrame的索引、列的并集:

>>> df1 + df2
            b   c     d   e
Colorado  NaN NaN   NaN NaN
Ohio      3.0 NaN   6.0 NaN
Oregon    NaN NaN   NaN NaN
Texas     9.0 NaN  12.0 NaN
Utah      NaN NaN   NaN NaN

由于'c'列和'e'列并不是两个DataFrame共有的列,这两列中产生了缺失值。对于行标签不同的DataFrame对象也是如此。

如果你将两个行或列完全不同的DataFrame对象相加,结果将全部为空:

>>> df1 = pd.DataFrame({'A': [1, 2]})
>>> df2 = pd.DataFrame({'B': [3, 4]})
>>> df1
   A
0  1
1  2

>>> df2
   B
0  3
1  4

>>> df1 + df2
    A   B
0 NaN NaN
1 NaN NaN

2.5.1 使用填充值的算术方法

在两个不同的索引化对象之间进行算术操作时,你可能会想要使用特殊填充值,比如当轴标签在一个对象中存在,在另一个对象中不存在时,你想将缺失值填充为0:

>>> df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
>>>                    columns=list('abcd'))
>>> df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
>>>                    columns=list('abcde'))
>>> df2.loc[1, 'b'] = np.nan
>>> df1
     a    b     c     d
0  0.0  1.0   2.0   3.0
1  4.0  5.0   6.0   7.0
2  8.0  9.0  10.0  11.0

>>> df2
      a     b     c     d     e
0   0.0   1.0   2.0   3.0   4.0
1   5.0   NaN   7.0   8.0   9.0
2  10.0  11.0  12.0  13.0  14.0
3  15.0  16.0  17.0  18.0  19.0

将这些df添加到一起会导致在一些不重叠的位置出现NA值:

>>> df1 + df2
      a     b     c     d   e
0   0.0   2.0   4.0   6.0 NaN
1   9.0   NaN  13.0  15.0 NaN
2  18.0  20.0  22.0  24.0 NaN
3   NaN   NaN   NaN   NaN NaN

在df1上使用add方法,将df2和一个fill_value作为参数传入:

>>> df1.add(df2, fill_value=0)
      a     b     c     d     e
0   0.0   2.0   4.0   6.0   4.0
1   9.0   5.0  13.0  15.0   9.0
2  18.0  20.0  22.0  24.0  14.0
3  15.0  16.0  17.0  18.0  19.0
>>> 1 / df1
          a         b         c         d
0       inf  1.000000  0.500000  0.333333
1  0.250000  0.200000  0.166667  0.142857
2  0.125000  0.111111  0.100000  0.090909

>>> df1.rdiv(1)
          a         b         c         d
0       inf  1.000000  0.500000  0.333333
1  0.250000  0.200000  0.166667  0.142857
2  0.125000  0.111111  0.100000  0.090909

对Series或DataFrame重建索引时,也可以指定一个不同的填充值:

>>> df1.reindex(columns=df2.columns, fill_value=0)
     a    b     c     d  e
0  0.0  1.0   2.0   3.0  0
1  4.0  5.0   6.0   7.0  0
2  8.0  9.0  10.0  11.0  0

灵活算术方法

2.5.2 DataFrame和Series间的操作

DataFrame和Series间的算术操作与NumPy中不同维度数组间的操作类似。

>>> arr = np.arange(12.).reshape((3, 4))
>>> arr
array([[  0.,   1.,   2.,   3.],
       [  4.,   5.,   6.,   7.],
       [  8.,   9.,  10.,  11.]])

>>> arr[0]
array([ 0.,  1.,  2.,  3.])

>>> arr - arr[0]
array([[ 0.,  0.,  0.,  0.],
       [ 4.,  4.,  4.,  4.],
       [ 8.,  8.,  8.,  8.]])

当我们从arr中减去arr[0]时,减法在每一行都进行了操作。这就是广播机制。

>>> frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
>>>                     columns=list('bde'),
>>>                    index=['Utah', 'Ohio', 'Texas', 'Oregon'])
>>> series = frame.iloc[0]
>>> frame
          b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0

>>> series
b    0.0
d    1.0
e    2.0
Name: Utah, dtype: float64

默认情况下,DataFrame和Series的数学操作中会将Series的索引和DataFrame的列进行匹配,并广播到各行:

>>> frame - series
          b    d    e
Utah    0.0  0.0  0.0
Ohio    3.0  3.0  3.0
Texas   6.0  6.0  6.0
Oregon  9.0  9.0  9.0

如果一个索引值不在DataFrame的列中,也不在Series的索引中,则对象会重建索引并形成联合:

>>> series2 = pd.Series(range(3), index=['b', 'e', 'f'])
>>> frame + series2
          b   d     e   f
Utah    0.0 NaN   3.0 NaN
Ohio    3.0 NaN   6.0 NaN
Texas   6.0 NaN   9.0 NaN
Oregon  9.0 NaN  12.0 NaN

如果想改为在列上进行广播,在行上匹配,必须使用算术方法中的一种。例如:

>>> series3 = frame['d']
>>> frame
          b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0

>>> series3
Utah       1.0
Ohio       4.0
Texas      7.0
Oregon    10.0
Name: d, dtype: float64

>>> frame.sub(series3, axis='index')
          b    d    e
Utah   -1.0  0.0  1.0
Ohio   -1.0  0.0  1.0
Texas  -1.0  0.0  1.0
Oregon -1.0  0.0  1.0

传递的axis值是用于匹配轴的。上面的示例中表示我们需要在DataFrame的行索引上对行匹配(axis='index'或axis=0),并进行广播。

2.6 函数应用和映射

NumPy的通用函数(逐元素数组方法)对pandas对象也有效:

>>> frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
>>>                    index=['Utah', 'Ohio', 'Texas', 'Oregon'])
>>> frame
               b         d         e
Utah   -0.204708  0.478943 -0.519439
Ohio   -0.555730  1.965781  1.393406
Texas   0.092908  0.281746  0.769023
Oregon  1.246435  1.007189 -1.296221

>>> np.abs(frame)
               b         d         e
Utah    0.204708  0.478943  0.519439
Ohio    0.555730  1.965781  1.393406
Texas   0.092908  0.281746  0.769023
Oregon  1.246435  1.007189  1.296221

另一个常用的操作是将函数应用到一行或一列的一维数组上。DataFrame的apply方法可以实现这个功能:

>>> f = lambda x: x.max() - x.min()
>>> frame.apply(f)
b    1.802165
d    1.684034
e    2.689627
dtype: float64

这里的函数f,可以计算Series最大值和最小值的差,会被frame中的每一列调用一次。结果是一个以frame的列作为索引的Series。

如果你传递axis='columns'给apply函数,函数将会被每行调用一次:

>>> frame.apply(f, axis='columns')
Utah      0.998382
Ohio      2.521511
Texas     0.676115
Oregon    2.542656
dtype: float64

大部分最常用的数组统计(比如sum和mean)都是DataFrame的方法,因此计算统计值时使用apply并不是必需的。

传递给apply的函数并不一定要返回一个标量值,也可以返回带有多个值的Series:

>>> def f(x):
>>>     return pd.Series([x.min(), x.max()], index=['min', 'max'])
>>> frame.apply(f)
            b         d         e
min -0.555730  0.281746 -1.296221
max  1.246435  1.965781  1.393406

逐元素的Python函数也可以使用。假设你想要根据frame中的每个浮点数计算一个格式化字符串,可以使用applymap方法:

>>> format = lambda x: '%.2f' % x
>>> frame.applymap(format)
            b     d      e
Utah    -0.20  0.48  -0.52
Ohio    -0.56  1.97   1.39
Texas    0.09  0.28   0.77
Oregon   1.25  1.01  -1.30

使用applymap作为函数名是因为Series有map方法,可以将一个逐元素的函数应用到Series上:

>>> frame['e'].map(format)
Utah      -0.52
Ohio       1.39
Texas      0.77
Oregon    -1.30
Name: e, dtype: object

2.7 排序和排名

根据某些准则对数据集进行排序是另一个重要的内建操作。如需按行或列索引进行字典型排序,需要使用sort_index方法,该方法返回一个新的、排序好的对象:

>>> obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
>>> obj.sort_index()
a    1
b    2
c    3
d    0
dtype: int64

在DataFrame中,你可以在各个轴上按索引排序:

>>> frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
>>>                      index=['three', 'one'],
>>>                      columns=['d', 'a', 'b', 'c'])

>>> frame.sort_index()
       d  a  b  c
one    4  5  6  7
three  0  1  2  3

>>> frame.sort_index(axis=1)
       a  b  c  d
three  1  2  3  0
one    5  6  7  4

数据默认会升序排序,但是也可以按照降序排序:

>>> frame.sort_index(axis=1, ascending=False)
       d  c  b  a
three  0  3  2  1
one    4  7  6  5

如果要根据Series的值进行排序,使用sort_values方法:

>>> obj = pd.Series([4, 7, -3, 2])
>>> obj.sort_values()
2   -3
3    2
0    4
1    7
dtype: int64

默认情况下,所有的缺失值都会被排序至Series的尾部:

>>> obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
>>> obj.sort_values()
4   -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

当对DataFrame排序时,你可以使用一列或多列作为排序键。为了实现这个功能,传递一个或多个列名给sort_values的可选参数by:        

>>> frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
>>> frame
   a  b
0  0  4
1  1  7
2  0 -3
3  1  2

>>> frame.sort_values(by='b')
   a  b
2  0 -3
3  1  2
0  0  4
1  1  7

对多列排序时,传递列名的列表:

>>> frame.sort_values(by=['a', 'b'])
   a  b
2  0 -3
0  0  4
3  1  2
1  1  7

排名是指对数组从1到有效数据点总数分配名次的操作。Series和DataFrame的rank方法是实现排名的方法,默认情况下,rank通过将平均排名分配到每个组来打破平级关系:

>>> obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
>>> obj.rank()
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64

排名也可以根据他们在数据中的观察顺序进行分配:

>>> obj.rank(method='first')
0    6.0
1    1.0
2    7.0
3    4.0
4    3.0
5    2.0
6    5.0
dtype: float64

在上面的例子中,对条目0和2设置的名次为6和7,而不是之前的平均排名6.5,是因为在数据中标签0在标签2的前面。

可以按降序排名:

# 将值分配给组中的最大排名
>>> obj.rank(ascending=False, method='max')
0    2.0
1    7.0
2    2.0
3    4.0
4    5.0
5    6.0
6    4.0
dtype: float64

DataFrame可以对行或列计算排名:

>>> frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
>>>                     'c': [-2, 5, 8, -2.5]})

>>> frame
   a    b    c
0  0  4.3 -2.0
1  1  7.0  5.0
2  0 -3.0  8.0
3  1  2.0 -2.5

>>> frame.rank(axis='columns')
     a    b    c
0  2.0  3.0  1.0
1  1.0  3.0  2.0
2  2.0  1.0  3.0
3  2.0  3.0  1.0

排名中的平级关系打破方法

2.8 含有重复标签的轴索引

>>> obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
>>> obj
a    0
a    1
b    2
b    3
c    4
dtype: int64

索引的is_unique属性可以告诉你它的标签是否唯一:

>>> obj.index.is_unique
False

带有重复索引的情况下,数据选择是与之前操作有差别的主要情况。根据一个标签索引多个条目会返回一个序列,而单个条目会返回标量值:

>>> obj['a']
a    0
a    1
dtype: int64

>>> obj['c']
4

这可能会使代码更复杂,因为来自索引的输出类型可能因标签是否重复而有所不同。

相同的逻辑可以拓展到在DataFrame中进行行索引:

>>> df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
>>> df
          0         1         2
a  0.274992  0.228913  1.352917
a  0.886429 -2.001637 -0.371843
b  1.669025 -0.438570 -0.539741
b  0.476985  3.248944 -1.021228

>>> df.loc['b']
          0         1         2
b  1.669025 -0.438570 -0.539741
b  0.476985  3.248944 -1.021228

3. 描述性统计的概述与计算

pandas对象装配了一个常用数学、统计学方法的集合。其中大部分属于归约或汇总统计的类别,这些方法从DataFrame的行或列中抽取一个Series或一系列值的单个值(如总和或平均值)。与NumPy数组中的类似方法相比,它们内建了处理缺失值的功能。

>>> df = pd.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='columns'或axis=1,则会将一行上各个列的值相加:

>>> df.sum(axis='columns')
a    1.40
b    2.60
c     NaN
d   -0.55
dtype: float64

除非整个切片上(在本例中是行或列)都是NA,否则NA值是被自动排除的。可以通过禁用skipna来实现不排除NA值:

>>> df.mean(axis='columns', skipna=False)
a      NaN
b    1.300
c      NaN
d   -0.275
dtype: float64

归约方法可选参数

 一些方法,比如idxmin和idxmax,返回的是间接统计信息,比如最小值或最大值的索引值:

>>> 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就是其中之一,它一次性产生多个汇总统计:

>>> df.describe()
            one       two
count  3.000000  2.000000
mean   3.083333 -2.900000
std    3.493685  2.262742
min    0.750000 -4.500000
25%    1.075000 -3.700000
50%    1.400000 -2.900000
75%    4.250000 -2.100000
max    7.100000 -1.300000

对于非数值型数据,describe产生另一种汇总统计:

>>> obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
>>> obj.describe()
count     16
unique     3
top        a
freq       8
dtype: object

描述性统计和汇总统计

3.1 相关性和协方差

一些汇总统计,比如相关性和协方差,是由多个参数计算出的。考虑某些使用附加pandas-datareader库从Yahoo!Finance上获取的包含股价和交易量的DataFrame。如果你还没有安装它,可以通过conda或pip进行安装:

conda install pandas-datareader
# 获得一些数据
>>> price = pd.read_pickle('examples/yahoo_price.pkl')
>>> volume = pd.read_pickle('examples/yahoo_volume.pkl')
>>> returns = price.pct_change()
>>> returns.tail()
                AAPL      GOOG       IBM      MSFT
Date                                              
2016-10-17 -0.000680  0.001837  0.002072 -0.003483
2016-10-18 -0.000681  0.019616 -0.026168  0.007690
2016-10-19 -0.002979  0.007846  0.003583 -0.002255
2016-10-20 -0.000512 -0.005652  0.001719 -0.004867
2016-10-21 -0.003930  0.003011 -0.012474  0.042096

Series的corr方法计算的是两个Series中重叠的、非NA的、按索引对齐的值的相关性。相应地,cov计算的是协方差:

>>> returns['MSFT'].corr(returns['IBM'])
0.49976361144151155

>>> returns['MSFT'].cov(returns['IBM'])
8.870655479703549e-05

由于MSFT是一个有效的Python属性,我们可以使用更为简洁的语法来获得这些数据:

>>> returns.MSFT.corr(returns.IBM)
0.49976361144151155

另一方面,DataFrame的corr和cov方法会分别以DataFrame的形式返回相关性和协方差矩阵:

>>> returns.corr()
          AAPL      GOOG       IBM      MSFT
AAPL  1.000000  0.407919  0.386817  0.389695
GOOG  0.407919  1.000000  0.405099  0.465919
IBM   0.386817  0.405099  1.000000  0.499764
MSFT  0.389695  0.465919  0.499764  1.000000

>>> returns.cov()
          AAPL      GOOG       IBM      MSFT
AAPL  0.000277  0.000107  0.000078  0.000095
GOOG  0.000107  0.000251  0.000078  0.000108
IBM   0.000078  0.000078  0.000146  0.000089
MSFT  0.000095  0.000108  0.000089  0.000215

使用DataFrame的corrwith方法,你可以计算出DataFrame中的行或列与另一个序列或DataFrame的相关性。该方法传入一个Series时,会返回一个含有为每列计算相关性值的Series:

>>> returns.corrwith(returns.IBM)
AAPL    0.386817
GOOG    0.405099
IBM     1.000000
MSFT    0.499764
dtype: float64

传入一个DataFrame时,会计算匹配到列名的相关性数值。在这里,计算出交易量百分比变化的相关性:

>>> returns.corrwith(volume)
AAPL   -0.075565
GOOG   -0.007067
IBM    -0.204849
MSFT   -0.092950
dtype: float64

传入axis='columns'会逐行地进行计算。在所有例子中,在计算相关性之前,数据点已经按标签进行了对齐。

3.2 唯一值、计数和成员属性

另一类相关的方法可以从一维Series包含的数值中提取信息。

>>> obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])

第一个函数是unique,它会给出Series中的唯一值:

>>> uniques = obj.unique()
>>> uniques
array(['c', 'a', 'd', 'b'], dtype=object)

唯一值并不一定按照排序好的顺序返回,但是如果需要的话可以进行排序(uniques.sort())。相应地,value_counts计算Series包含的值的个数:

>>> obj.value_counts()
c    3
a    3
b    2
d    1
dtype: int64

为了方便,返回的Series会按照数量降序排序。value_counts也是有效的pandas顶层方法,可以用于任意数组或序列:

>>> pd.value_counts(obj.values, sort=False)
b    2
c    3
d    1
a    3
dtype: int64

isin执行向量化的成员属性检查,还可以将数据集以Series或DataFrame一列的形式过滤为数据集的值子集:

>>> obj
0    c
1    a
2    d
3    a
4    a
5    b
6    b
7    c
8    c
dtype: object

>>> mask = obj.isin(['b', 'c'])
>>> 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相关的Index.get_indexer方法,可以提供一个索引数组,这个索引数组可以将可能非唯一值数组转换为另一个唯一值数组:

>>> to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
>>> unique_vals = pd.Series(['c', 'b', 'a'])
>>> pd.Index(unique_vals).get_indexer(to_match)
array([0, 2, 1, 1, 0, 2])

唯一值、计数和集合成员属性方法

 某些情况下,你可能想要计算DataFrame多个相关列的直方图,如下面的例子:

>>> data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
>>>                      'Qu2': [2, 3, 1, 2, 3],
>>>                      'Qu3': [1, 5, 2, 4, 4]})

>>> data
   Qu1  Qu2  Qu3
0    1    2    1
1    3    3    5
2    4    1    2
3    3    2    4
4    4    3    4

将pandas.value_counts传入DataFrame的apply函数可以得到:

# 这里,结果中的行标签是所有列中出现的不同值,数值则是这些不同值在每个列中出现的次数。
>>> result = data.apply(pd.value_counts).fillna(0)
>>> result
   Qu1  Qu2  Qu3
1  1.0  1.0  1.0
2  0.0  2.0  1.0
3  2.0  2.0  0.0
4  2.0  0.0  2.0
5  0.0  0.0  1.0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南河Aure

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值