Python pandas 0.19.1 Intro to Data Structures 数据结构介绍 文档翻译

官方文档链接http://pandas.pydata.org/pandas-docs/stable/dsintro.html

数据结构介绍

我们将以一个快速的、非全面的pandas的基础数据结构概述来开始。应用在所有对象的数据类型、索引和轴标签/对齐等的基础操作。首先我们需要向你的命名空间引入numpy和pandas。

In [1]: import numpy as np
In [2]: import pandas as pd

有个宗旨需要牢记:数据对齐是内在的。标签和数据间的链接不会被轻易改变,除非你明确地做了相应的操作来让它改变。

我们将会对数据结构进行简要的介绍,然后会考虑到单独部分的功能和方法。

Series

Series 是一个一维的、可容纳任何数据类型(整型,字符串,浮点型数据,Python对象等)的标记数组。它的轴标签统称为索引。最基本的创建一个Series的方法是:

>>> s = pd.Series(data, index=index)

在这里,data可以是多种事物:

  • 一个Python字典
  • 一个多维数组
  • 一个标量值(如5)

传入的index是一个轴标签列表。因此,根据data的不同,有如下几种情况:

基于多维数组

如果data是一个多维数组,index必须和data的长度相同。如果没有传入index,系统将会自动创建一个index,其值为[0, ..., len(data) - 1].

In [3]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [4]: s
Out[4]: 
a    0.2735
b    0.6052
c   -0.1692
d    1.8298
e    0.5432
dtype: float64

In [5]: s.index
Out[5]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object')

In [6]: pd.Series(np.random.randn(5))
Out[6]: 
0    0.3674
1   -0.8230
2   -1.0295
3   -1.0523
4   -0.8502
dtype: float64

注意

从v0.8.0版本开始,pandas开始支持非唯一索引值。如果尝试一个不支持重复索引值的操作,将会引发一个异常。懒惰的原因几乎都是源于性能(有许多计算中的实例,如GroupBy中的一部分就不需要用到index)

基于字典

如果data是一个字典,如果向index 中传入的值是data中的,那么index中对应于标签数据的值将会被移出。否则,如果可能的话,将会构成一个基于排序字典的index。

In [7]: d = {'a' : 0., 'b' : 1., 'c' : 2.}

In [8]: pd.Series(d)
Out[8]: 
a    0.0
b    1.0
c    2.0
dtype: float64

In [9]: pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[9]: 
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64

注意

NaN在pandas代表缺失值。

基于标量值

如果data是一个标量值,就必须传入一个index。因为这个标量值的数量取决于index的长度。

In [10]: pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
Out[10]: 
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64

多维数组型Series

Series 非常像一个多维数组,拥有着numpy的大部分功能。然而,切片操作也会对索引进行切片。

In [11]: s[0]
Out[11]: 0.27348116325673794

In [12]: s[:3]
Out[12]: 
a    0.2735
b    0.6052
c   -0.1692
dtype: float64

In [13]: s[s > s.median()]
Out[13]: 
b    0.6052
d    1.8298
dtype: float64

In [14]: s[[4, 3, 1]]
Out[14]: 
e    0.5432
d    1.8298
b    0.6052
dtype: float64

In [15]: np.exp(s)
Out[15]: 
a    1.3145
b    1.8317
c    0.8443
d    6.2327
e    1.7215
dtype: float64

我们将在一个单独的 section介绍基于数组的索引。

字典型Series

一个Series就像一个固定大小的字典,你可以通过索引标签来获取和设置Series的值:

In [16]: s['a']
Out[16]: 0.27348116325673794

In [17]: s['e'] = 12.

In [18]: s
Out[18]: 
a     0.2735
b     0.6052
c    -0.1692
d     1.8298
e    12.0000
dtype: float64

In [19]: 'e' in s
Out[19]: True

In [20]: 'f' in s
Out[20]: False

如果一个标签不包含在其中,将会引发一个异常:

>>> s['f']
KeyError: 'f'

使用get方法,缺失的标签将会返回None或者指定的缺省值:

In [21]: s.get('f')

In [22]: s.get('f', np.nan)
Out[22]: nan

参见属性访问部分 section on attribute access.

Series的量化操作和标签对齐

当进行数据分析时,通过Series的一个又一个值进行循环通常是没有必要的。Series能够使用大部分的numpy方法。

In [23]: s + s
Out[23]: 
a     0.5470
b     1.2104
c    -0.3385
d     3.6596
e    24.0000
dtype: float64

In [24]: s * 2
Out[24]: 
a     0.5470
b     1.2104
c    -0.3385
d     3.6596
e    24.0000
dtype: float64

In [25]: np.exp(s)
Out[25]: 
a         1.3145
b         1.8317
c         0.8443
d         6.2327
e    162754.7914
dtype: float64

Series和多维数组的关键区别在于Series的数据对齐是基于标签的。因此,不管你的Series是不是有相同的标签,你都可以无所顾忌地进行计算。

In [26]: s[1:] + s[:-1]
Out[26]: 
a       NaN
b    1.2104
c   -0.3385
d    3.6596
e       NaN
dtype: float64


在未对齐的Series进行操作将会涉及到索引的联合。如果一个标签在一个Series或其他中都不存在,在结果中将会被标记为缺失值。能够在未做任何明确的数据对齐的情况下编写代码,为交互式数据分析和研究提供了极大的自由和灵活性。

注意

一般来说,为了避免信息错乱,我们选择在不同的索引对象间的操作的默认结果产生索引的联合。

name属性

Series还有一个name属性:

In [27]: s = pd.Series(np.random.randn(5), name='something')

In [28]: s
Out[28]: 
0    1.5140
1   -1.2345
2    0.5666
3   -1.0184
4    0.1081
Name: something, dtype: float64

In [29]: s.name
Out[29]: 'something'

Series的name属性在很多情况下会被自动分配,特别是在你在下面看到的dataframe的一维切片中。

0.18.0.的新版本。

你可以通过 pandas.Series.rename() 方法来对Series重命名。

In [30]: s2 = s.rename("different")

In [31]: s2.name
Out[31]: 'different'

注意s和s2是不同的对象。

DataFrame

DataFrame 是一个拥有不同类型的列的二维的标记数据结构。你可以把它想成一个电子表格或数据库表,或者一个Seires对象的字典。它是pandas对象中最常用的一种。和Series一样,dataframe能够接受不同类型的输入:

  • 一维数组,列表或Seires的字典
  • 二维的bumpy数组
  • Structured or record结构化或记录数组
  • 一个 Series
  • 另外一个DataFrame

对于dataframe中的data, 你可以选择传入index(行标签)和columns(列标签)。如果你传入了一个index和/或columns,你必须保证你传入的生成data frame的index和/或columns。因此,一个Series字典加上一个明确的索引将会丢弃所有不匹配索引的数据。

如果没有传入轴标签,系统将会基于常识规则根据输入的数据构造轴标签。

基于Series字典或字典的data frame

最终index是多种Series的index的联合。如果有任何嵌套字典,将会首先被转换成Series。如果没有传入列,列将会是字典的键的排序列表。

In [32]: d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
   ....:      'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
   ....: 

In [33]: df = pd.DataFrame(d)

In [34]: df
Out[34]: 
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In [35]: pd.DataFrame(d, index=['d', 'b', 'a'])
Out[35]: 
   one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In [36]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[36]: 
   two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN

行和列标签能够分别通过访问index和columns属性进行访问。

注意

 当一个特殊的列集合和字典同时被传入时,传入的列将会重写字典中的键。
In [37]: df.index
Out[37]: Index([u'a', u'b', u'c', u'd'], dtype='object')

In [38]: df.columns
Out[38]: Index([u'one', u'two'], dtype='object')

基于多维数组/列表的字典的data frame

多维数组必须都是相同长度的,如果传入了一个index,它必须和数组的长度相同。如果没有传入index,那么自动生成的index就是range(n),其中n是数组的长度。

In [39]: d = {'one' : [1., 2., 3., 4.],
   ....:      'two' : [4., 3., 2., 1.]}
   ....: 

In [40]: pd.DataFrame(d)
Out[40]: 
   one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

In [41]: pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out[41]: 
   one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0

基于结构化或记录数组的data frame

这种情况和数组字典的情况相同。

In [42]: data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')])

In [43]: data[:] = [(1,2.,'Hello'), (2,3.,"World")]

In [44]: pd.DataFrame(data)
Out[44]: 
   A    B      C
0  1  2.0  Hello
1  2  3.0  World

In [45]: pd.DataFrame(data, index=['first', 'second'])
Out[45]: 
        A    B      C
first   1  2.0  Hello
second  2  3.0  World

In [46]: pd.DataFrame(data, columns=['C', 'A', 'B'])
Out[46]: 
       C  A    B
0  Hello  1  2.0
1  World  2  3.0

注意

DataFrame 并不打算向二维bumpy数组那样.

基于字典列表的data frame

In [47]: data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]

In [48]: pd.DataFrame(data2)
Out[48]: 
   a   b     c
0  1   2   NaN
1  5  10  20.0

In [49]: pd.DataFrame(data2, index=['first', 'second'])
Out[49]: 
        a   b     c
first   1   2   NaN
second  5  10  20.0

In [50]: pd.DataFrame(data2, columns=['a', 'b'])
Out[50]: 
   a   b
0  1   2
1  5  10

基于集合字典的data frame

你可以通过传入一个集合字典来自动创建一个多索引的frame。

In [51]: pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
   ....:               ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
   ....:               ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
   ....:               ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
   ....:               ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
   ....: 
Out[51]: 
       a              b      
       a    b    c    a     b
A B  4.0  1.0  5.0  8.0  10.0
  C  3.0  2.0  6.0  7.0   NaN
  D  NaN  NaN  NaN  NaN   9.0

基于一个Series

创建的结果是一个和输入的Series有着相同index的dataframe ,其中一列的名字是Series的原始名字(如果其他列的名字没有提供的话)

缺失数据

更多详见 Missing data 部分。使用np.nan能够构造一个包含缺失值的data frame。或者,你还可以传入一个numpy.MaskedArray,它的mask项目将被视为缺失值。

交替的构造函数

DataFrame.from_dict

DataFrame.from_dict 接收一个字典或一个数组序列的字典,并返回一个data frame。 It operates like the DataFrame constructor except for the orient parameter which is 'columns' by default, but which can be set to 'index' in order to use the dict keys as row labels.

DataFrame.from_records

DataFrame.from_records 接收一个集合列表或结构化类型的多维数组。它与正常的dataframe构造器类似,不同的是它的index或许是一个特定的结构化类型。例如:

In [52]: data
Out[52]: 
array([(1, 2.0, 'Hello'), (2, 3.0, 'World')], 
      dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

In [53]: pd.DataFrame.from_records(data, index='C')
Out[53]: 
       A    B
C            
Hello  1  2.0
World  2  3.0

DataFrame.from_items

DataFrame.from_items的形式也与dataframe构造器类似,它接收一个(键,值)序列,其中键是列名(或行名,如果零orient=“index”的话),值是列值(或行值)。这对于构造一个特定顺序列dataframe来说非常有用。

In [54]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])])
Out[54]: 
   A  B
0  1  4
1  2  5
2  3  6

如果你传入 orient='index’关键字,值就会变成行标签。但是这种情况下你必须接着传入列名:

In [55]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
   ....:                         orient='index', columns=['one', 'two', 'three'])
   ....: 
Out[55]: 
   one  two  three
A    1    2      3
B    4    5      6

列的选取,增加和删除

你可以使用一个相同indexed的Series对象的字典的语法对dataframe进行操作。获取,设置和删除列的语法和字典操作类似:

In [56]: df['one']
Out[56]: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64

In [57]: df['three'] = df['one'] * df['two']

In [58]: df['flag'] = df['one'] > 2

In [59]: df
Out[59]: 
   one  two  three   flag
a  1.0  1.0    1.0  False
b  2.0  2.0    4.0  False
c  3.0  3.0    9.0   True
d  NaN  4.0    NaN  False

列可以向一个字典那样被删除或移除:

In [60]: del df['two']

In [61]: three = df.pop('three')

In [62]: df
Out[62]: 
   one   flag
a  1.0  False
b  2.0  False
c  3.0   True
d  NaN  False


当想要插入一个标量值时,它会自动填充整列:

In [63]: df['foo'] = 'bar'

In [64]: df
Out[64]: 
   one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar

当插入一个与当前dataframe有着不同index的Series时,将会以dataframe的index为基准。

In [65]: df['one_trunc'] = df['one'][:2]

In [66]: df
Out[66]: 
   one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN

你可以插入原始多维数组,但是它们的长度必须和dataframe的index的长度相匹配。

默认情况下会在末尾插入列。inser功能允许在特定位置插入列:

In [67]: df.insert(1, 'bar', df['one'])

In [68]: df
Out[68]: 
   one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN

在方法链中分配新列

受 dplyr’s 动词的变异, DataFrame 有一个 assign() 方法能够允许你轻易地创建一个从现有列派生出的新列。

In [69]: iris = pd.read_csv('data/iris.data')

In [70]: iris.head()
Out[70]: 
   SepalLength  SepalWidth  PetalLength  PetalWidth         Name
0          5.1         3.5          1.4         0.2  Iris-setosa
1          4.9         3.0          1.4         0.2  Iris-setosa
2          4.7         3.2          1.3         0.2  Iris-setosa
3          4.6         3.1          1.5         0.2  Iris-setosa
4          5.0         3.6          1.4         0.2  Iris-setosa

In [71]: (iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength'])
   ....:      .head())
   ....: 
Out[71]: 
   SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa       0.6863
1          4.9         3.0          1.4         0.2  Iris-setosa       0.6122
2          4.7         3.2          1.3         0.2  Iris-setosa       0.6809
3          4.6         3.1          1.5         0.2  Iris-setosa       0.6739
4          5.0         3.6          1.4         0.2  Iris-setosa       0.7200

以上是插入一个预先计算好的值的例子。我们也可以传入一个单参数的函数.

In [72]: iris.assign(sepal_ratio = lambda x: (x['SepalWidth'] /
   ....:                                      x['SepalLength'])).head()
   ....: 
Out[72]: 
   SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa       0.6863
1          4.9         3.0          1.4         0.2  Iris-setosa       0.6122
2          4.7         3.2          1.3         0.2  Iris-setosa       0.6809
3          4.6         3.1          1.5         0.2  Iris-setosa       0.6739
4          5.0         3.6          1.4         0.2  Iris-setosa       0.7200

assign 总是 returns返回一个数据的副本,而不会改变原data frame. 

当你手边没有一个参考的dataframe时,相对于要插入的实际值来说,传入一个可变的值是非常有用的。在链式操作中使用assign是非常普遍的。例如,我们可以限制dataframe只选择那些长度大于5的SepalLength进行计算和作图:

In [73]: (iris.query('SepalLength > 5')
   ....:      .assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength,
   ....:              PetalRatio = lambda x: x.PetalWidth / x.PetalLength)
   ....:      .plot(kind='scatter', x='SepalRatio', y='PetalRatio'))
   ....: 
Out[73]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff286891b50>

由于传入了一个函数,该函数是在被分配到的dataframe上进行计算,重要的是,这个dataframe是那些长度大于5的行的dataframe.首先进行过滤,然后进行计算。在这个例子中,我们没有可过滤的dataframe。

assign的函数签名是简单的**kwargs。键是新字段的列名,值是用来插入的值(例如一个Series或者numpy数组),或者一个单参数的函数。将会返回一个插入了新值的dataframe的副本。

警告

由于assign的函数签名是**kwargs,是个字典,那么在生成的dataframe中插入的新列的顺序不能够保证和你传入的顺序一样。一般是在dataframe的最后按字母顺序插入。

所有的表达式会被首先计算,然后被分配。所以你不能在同一个assign中使用正在分配的列。

In [74]: # 不要这样做
        df.assign(C = lambda x: x['A'] + x['B'],
                  D = lambda x: x['A'] + x['C'])
In [2]: # 而是需要把它分给两个assign
        (df.assign(C = lambda x: x['A'] + x['B'])
           .assign(D = lambda x: x['A'] + x['C']))


索引/选取

基本的索引如下所示:

操作语法结果
选取列 df[col] Series
通过标签选取行 df.loc[label] Series
通过整数位置选取行 df.iloc[loc] Series
行切片 df[5:10] DataFrame
通过布尔向量选取行 df[bool_vec] DataFrame

选取行,例如,返回一个Series,其index就是dataframe的列。

In [75]: df.loc['b']
Out[75]: 
one              2
bar              2
flag         False
foo            bar
one_trunc        2
Name: b, dtype: object

In [76]: df.iloc[2]
Out[76]: 
one             3
bar             3
flag         True
foo           bar
one_trunc     NaN
Name: c, dtype: object

对于更复杂的基于标签的索引和切片的更详尽的处理,参见 section on indexing

数据对齐和算法

dataframe对象间的数据对齐会根据索引(行标签)和列自动对齐。同样,产生的结果将是行和列标签的联合。

In [77]: df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])

In [78]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])

In [79]: df + df2
Out[79]: 
        A       B       C   D
0  0.5222  0.3225 -0.7566 NaN
1 -0.8441  0.2334  0.8818 NaN
2 -2.2079 -0.1572 -0.3875 NaN
3  2.8080 -1.0927  1.0432 NaN
4 -1.7511 -2.0812  2.7477 NaN
5 -3.2473 -1.0850  0.7898 NaN
6 -1.7107  0.0661  0.1294 NaN
7     NaN     NaN     NaN NaN
8     NaN     NaN     NaN NaN
9     NaN     NaN     NaN NaN

当在dataframe和Series间进行操作时,默认操作是将Series的index和dataframe的列对齐,并broadcasting 行。例如:

In [80]: df - df.iloc[0]
Out[80]: 
        A       B       C       D
0  0.0000  0.0000  0.0000  0.0000
1 -2.6396 -1.0702  1.7214 -0.7896
2 -2.7662 -1.6918  2.2776 -2.5401
3  0.8679 -3.5247  1.9365 -0.1331
4 -1.9883 -3.2162  2.0464 -1.0700
5 -3.3932 -4.0976  1.6366 -2.1635
6 -1.3668 -1.9572  1.6523 -0.7191
7 -0.7949 -2.1663  0.9706 -2.6297
8 -0.8383 -1.3630  1.6702 -2.0865
9  0.8588  0.0814  3.7305 -1.3737

在对时间序列数据进行操作的特殊情况中,DataFrame 的index包含了日期,因此broadcasting将会对列进行:

In [81]: index = pd.date_range('1/1/2000', periods=8)

In [82]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))

In [83]: df
Out[83]: 
                 A       B       C
2000-01-01  0.2731  0.3604 -1.1515
2000-01-02  1.1577  1.4787 -0.6528
2000-01-03 -0.7712  0.2203 -0.5739
2000-01-04 -0.6356 -1.1703 -0.0789
2000-01-05 -1.4687  0.1705 -1.8796
2000-01-06 -1.2037  0.9568 -1.1383
2000-01-07 -0.6540 -0.2169  0.3843
2000-01-08 -2.1639 -0.8145 -1.2475

In [84]: type(df['A'])
Out[84]: pandas.core.series.Series

In [85]: df - df['A']
Out[85]: 
            2000-01-01 00:00:00  2000-01-02 00:00:00  2000-01-03 00:00:00  \
2000-01-01                  NaN                  NaN                  NaN   
2000-01-02                  NaN                  NaN                  NaN   
2000-01-03                  NaN                  NaN                  NaN   
2000-01-04                  NaN                  NaN                  NaN   
2000-01-05                  NaN                  NaN                  NaN   
2000-01-06                  NaN                  NaN                  NaN   
2000-01-07                  NaN                  NaN                  NaN   
2000-01-08                  NaN                  NaN                  NaN   

            2000-01-04 00:00:00 ...  2000-01-08 00:00:00   A   B   C  
2000-01-01                  NaN ...                  NaN NaN NaN NaN  
2000-01-02                  NaN ...                  NaN NaN NaN NaN  
2000-01-03                  NaN ...                  NaN NaN NaN NaN  
2000-01-04                  NaN ...                  NaN NaN NaN NaN  
2000-01-05                  NaN ...                  NaN NaN NaN NaN  
2000-01-06                  NaN ...                  NaN NaN NaN NaN  
2000-01-07                  NaN ...                  NaN NaN NaN NaN  
2000-01-08                  NaN ...                  NaN NaN NaN NaN  

[8 rows x 11 columns]

警告

df - df['A']

现在已不再使用,并将会在以后的版本中移除。该操作正确的方法是:

df.sub(df['A'], axis=0)

对匹配和广播行为的显式控制,参见 flexible binary operations.

你期待的对标量的操作:

In [86]: df * 5 + 2
Out[86]: 
                 A       B       C
2000-01-01  3.3655  3.8018 -3.7575
2000-01-02  7.7885  9.3936 -1.2641
2000-01-03 -1.8558  3.1017 -0.8696
2000-01-04 -1.1781 -3.8513  1.6056
2000-01-05 -5.3437  2.8523 -7.3982
2000-01-06 -4.0186  6.7842 -3.6915
2000-01-07 -1.2699  0.9157  3.9217
2000-01-08 -8.8194 -2.0724 -4.2375

In [87]: 1 / df
Out[87]: 
                 A       B        C
2000-01-01  3.6616  2.7751  -0.8684
2000-01-02  0.8638  0.6763  -1.5318
2000-01-03 -1.2967  4.5383  -1.7424
2000-01-04 -1.5733 -0.8545 -12.6759
2000-01-05 -0.6809  5.8662  -0.5320
2000-01-06 -0.8308  1.0451  -0.8785
2000-01-07 -1.5291 -4.6113   2.6019
2000-01-08 -0.4621 -1.2278  -0.8016

In [88]: df ** 4
Out[88]: 
                  A       B           C
2000-01-01   0.0056  0.0169  1.7581e+00
2000-01-02   1.7964  4.7813  1.8162e-01
2000-01-03   0.3537  0.0024  1.0849e-01
2000-01-04   0.1632  1.8755  3.8733e-05
2000-01-05   4.6534  0.0008  1.2482e+01
2000-01-06   2.0995  0.8382  1.6789e+00
2000-01-07   0.1829  0.0022  2.1819e-02
2000-01-08  21.9244  0.4401  2.4219e+00

布尔操作也是如此:

In [89]: df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)

In [90]: df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)

In [91]: df1 & df2
Out[91]: 
       a      b
0  False  False
1  False   True
2   True  False

In [92]: df1 | df2
Out[92]: 
      a     b
0  True  True
1  True  True
2  True  True

In [93]: df1 ^ df2
Out[93]: 
       a      b
0   True   True
1   True  False
2  False   True

In [94]: -df1
Out[94]: 
       a      b
0  False   True
1   True  False
2  False  False

转置

转置可以通过T属性进行,和一个多维数组相似:

# 只显示前5行
In [95]: df[:5].T
Out[95]: 
   2000-01-01  2000-01-02  2000-01-03  2000-01-04  2000-01-05
A      0.2731      1.1577     -0.7712     -0.6356     -1.4687
B      0.3604      1.4787      0.2203     -1.1703      0.1705
C     -1.1515     -0.6528     -0.5739     -0.0789     -1.8796

DataFrame与 NumPy功能的交互

如果dataframe中的数据是数值型的话,Numpy的功能(如log, exp, sort等)和多种其他的numpy功能可以没有问题地使用在dataframe上。

In [96]: np.exp(df)
Out[96]: 
                 A       B       C
2000-01-01  1.3140  1.4338  0.3162
2000-01-02  3.1826  4.3873  0.5206
2000-01-03  0.4625  1.2465  0.5633
2000-01-04  0.5296  0.3103  0.9241
2000-01-05  0.2302  1.1859  0.1526
2000-01-06  0.3001  2.6034  0.3204
2000-01-07  0.5200  0.8050  1.4686
2000-01-08  0.1149  0.4429  0.2872

In [97]: np.asarray(df)
Out[97]: 
array([[ 0.2731,  0.3604, -1.1515],
       [ 1.1577,  1.4787, -0.6528],
       [-0.7712,  0.2203, -0.5739],
       [-0.6356, -1.1703, -0.0789],
       [-1.4687,  0.1705, -1.8796],
       [-1.2037,  0.9568, -1.1383],
       [-0.654 , -0.2169,  0.3843],
       [-2.1639, -0.8145, -1.2475]])

dataframe中的dot方法能够实现矩阵相乘。

In [98]: df.T.dot(df)
Out[98]: 
         A       B       C
A  11.1298  2.8864  6.0015
B   2.8864  5.3895 -1.8913
C   6.0015 -1.8913  8.6204

相似地,Series的dot方法能够执行点积:

In [99]: s1 = pd.Series(np.arange(5,10))

In [100]: s1.dot(s1)
Out[100]: 255

dataframe并不旨在做多维数组的替代,因为它的索引语法在矩阵方面和多维数组大不相同。

控制台显示

大规模的dataframe在控制台显示时将会被截断。你可以使用info()来获取一个概括。(下面我读取一个csv试图的baseball数据集)

In [101]: baseball = pd.read_csv('data/baseball.csv')

In [102]: print(baseball)
       id     player  year  stint  ...   hbp   sh   sf  gidp
0   88641  womacto01  2006      2  ...   0.0  3.0  0.0   0.0
1   88643  schilcu01  2006      1  ...   0.0  0.0  0.0   0.0
..    ...        ...   ...    ...  ...   ...  ...  ...   ...
98  89533   aloumo01  2007      1  ...   2.0  0.0  3.0  13.0
99  89534  alomasa02  2007      1  ...   0.0  0.0  0.0   0.0

[100 rows x 23 columns]

In [103]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
id        100 non-null int64
player    100 non-null object
year      100 non-null int64
stint     100 non-null int64
team      100 non-null object
lg        100 non-null object
g         100 non-null int64
ab        100 non-null int64
r         100 non-null int64
h         100 non-null int64
X2b       100 non-null int64
X3b       100 non-null int64
hr        100 non-null int64
rbi       100 non-null float64
sb        100 non-null float64
cs        100 non-null float64
bb        100 non-null int64
so        100 non-null float64
ibb       100 non-null float64
hbp       100 non-null float64
sh        100 non-null float64
sf        100 non-null float64
gidp      100 non-null float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.0+ KB

然而,使用to_string将会返回一个表格中的dataframe的字符串表示,虽然它不会总是符合控制台的宽度:

In [104]: print(baseball.iloc[-20:, :12].to_string())
       id     player  year  stint team  lg    g   ab   r    h  X2b  X3b
80  89474  finlest01  2007      1  COL  NL   43   94   9   17    3    0
81  89480  embreal01  2007      1  OAK  AL    4    0   0    0    0    0
82  89481  edmonji01  2007      1  SLN  NL  117  365  39   92   15    2
83  89482  easleda01  2007      1  NYN  NL   76  193  24   54    6    0
84  89489  delgaca01  2007      1  NYN  NL  139  538  71  139   30    0
85  89493  cormirh01  2007      1  CIN  NL    6    0   0    0    0    0
86  89494  coninje01  2007      2  NYN  NL   21   41   2    8    2    0
87  89495  coninje01  2007      1  CIN  NL   80  215  23   57   11    1
88  89497  clemero02  2007      1  NYA  AL    2    2   0    1    0    0
89  89498  claytro01  2007      2  BOS  AL    8    6   1    0    0    0
90  89499  claytro01  2007      1  TOR  AL   69  189  23   48   14    0
91  89501  cirilje01  2007      2  ARI  NL   28   40   6    8    4    0
92  89502  cirilje01  2007      1  MIN  AL   50  153  18   40    9    2
93  89521  bondsba01  2007      1  SFN  NL  126  340  75   94   14    0
94  89523  biggicr01  2007      1  HOU  NL  141  517  68  130   31    3
95  89525  benitar01  2007      2  FLO  NL   34    0   0    0    0    0
96  89526  benitar01  2007      1  SFN  NL   19    0   0    0    0    0
97  89530  ausmubr01  2007      1  HOU  NL  117  349  38   82   16    3
98  89533   aloumo01  2007      1  NYN  NL   87  328  51  112   19    1
99  89534  alomasa02  2007      1  NYN  NL    8   22   1    3    1    0


从0.10.0版本开始,列数过多的dataframes将默认多行显示:

In [105]: pd.DataFrame(np.random.randn(3, 12))
Out[105]: 
         0         1         2         3         4         5         6   \
0  2.173014  1.273573  0.888325  0.631774  0.206584 -1.745845 -0.505310   
1 -1.240418  2.177280 -0.082206  0.827373 -0.700792  0.524540 -1.101396   
2  0.269598 -0.453050 -1.821539 -0.126332 -0.153257  0.405483 -0.504557   

         7         8         9         10        11  
0  1.376623  0.741168 -0.509153 -2.012112 -1.204418  
1  1.115750  0.294139  0.286939  1.709761 -0.212596  
2  1.405148  0.778061 -0.799024 -0.670727  0.086877  

你可以通过设置display.width选项来改变每一行显示的列数量:

In [106]: pd.set_option('display.width', 40) # 默认为80

In [107]: pd.DataFrame(np.random.randn(3, 12))
Out[107]: 
         0         1         2   \
0  1.179465  0.777427 -1.923460   
1  0.054928  0.776156  0.372060   
2 -0.243404 -1.506557 -1.977226   

         3         4         5   \
0  0.782432  0.203446  0.250652   
1  0.710963 -0.784859  0.168405   
2 -0.226582 -0.777971  0.231309   

         6         7         8   \
0 -2.349580 -0.540814 -0.748939   
1  0.159230  0.866492  1.266025   
2  1.394479  0.723474 -0.097256   

         9         10        11  
0 -0.994345  1.478624 -0.341991  
1  0.555240  0.731803  0.219383  
2  0.375274 -0.314401 -2.363136  


你可以通过设置display.max_colwidth来调整每一列的最大宽度:

In [108]: datafile={'filename': ['filename_01','filename_02'],
   .....:           'path': ["media/user_name/storage/folder_01/filename_01",
   .....:                    "media/user_name/storage/folder_02/filename_02"]}
   .....: 

In [109]: pd.set_option('display.max_colwidth',30)

In [110]: pd.DataFrame(datafile)
Out[110]: 
      filename  \
0  filename_01   
1  filename_02   

                            path  
0  media/user_name/storage/fo...  
1  media/user_name/storage/fo...  

In [111]: pd.set_option('display.max_colwidth',100)

In [112]: pd.DataFrame(datafile)
Out[112]: 
      filename  \
0  filename_01   
1  filename_02   

                                            path  
0  media/user_name/storage/folder_01/filename_01  
1  media/user_name/storage/folder_02/filename_02  

你也可以通过expand_frame_repr 选项来禁用此功能。这将会在一个块中打印表格。

DataFrame 的列属性获取和IPython实现

如果一个dataframe列标签是一个有效的Python变量名,该列就能够通过属性的方式访问:

In [113]: df = pd.DataFrame({'foo1' : np.random.randn(5),
   .....:                    'foo2' : np.random.randn(5)})
   .....: 

In [114]: df
Out[114]: 
       foo1      foo2
0 -0.412237  0.213232
1 -0.237644  1.740139
2  1.272869 -0.241491
3  1.220450 -0.868514
4  1.315172  0.407544

In [115]: df.foo1
Out[115]: 
0   -0.412237
1   -0.237644
2    1.272869
3    1.220450
4    1.315172
Name: foo1, dtype: float64

列还与 Python 实现机制相连,所以它们能够通过tab键实现自动完成。

In [5]: df.fo<TAB>
df.foo1  df.foo2

Panel

Panel是一个用处不多但仍旧很重要的三维数据容器。词条panel data 来自于计量经济学,并且是pandas这个名称的一部分来源( pan(el)-da(ta)-s)。panel的3个轴的名称旨在为包含panel数据的描述操作提供一些语义,尤其是panel数据的计量分析。然而,为了交叉分析dataframe对象集,你可能会发现panel的轴名称有点武断:

  • : 0轴,每一项对应于一个包含于其中的dataframe
  • 主轴: 1轴, 是每个data frame的index (行) 
  • 次轴: 2轴,是每个data frame的columns

Panels的构成也正如你想象的那样: 

基于可选轴标签的三维数组

In [116]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'],
   .....:               major_axis=pd.date_range('1/1/2000', periods=5),
   .....:               minor_axis=['A', 'B', 'C', 'D'])
   .....: 

In [117]: wp
Out[117]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D

基于dataframe对象字典的panel

In [118]: data = {'Item1' : pd.DataFrame(np.random.randn(4, 3)),
   .....:         'Item2' : pd.DataFrame(np.random.randn(4, 2))}
   .....: 

In [119]: pd.Panel(data)
Out[119]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 4 (major_axis) x 3 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 0 to 3
Minor_axis axis: 0 to 2

注意,字典中的值只需要可转换为dataframe。因此,它们可以是dataframe中任何有效的输入。

一个有用的工厂方法是Panel.from_dict, 它能够接受一个dataframe字典和下列命名参数:

参数默认描述
intersect False 删除指数不对齐的元素
orient items 使dataframe的列作为panel的项 

例如,与上面的结构相比:

In [120]: pd.Panel.from_dict(data, orient='minor')
Out[120]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 4 (major_axis) x 2 (minor_axis)
Items axis: 0 to 2
Major_axis axis: 0 to 3
Minor_axis axis: Item1 to Item2

Orient 对于混合类型的dataframe特别有用。如果你传入了一个其列是混合类型dataframe对象,除非你传入orient=‘minor’,否则所有的数据类型都将会向上变为object类型。

In [121]: df = pd.DataFrame({'a': ['foo', 'bar', 'baz'],
   .....:                    'b': np.random.randn(3)})
   .....: 

In [122]: df
Out[122]: 
     a         b
0  foo -1.142863
1  bar -1.015321
2  baz  0.683625

In [123]: data = {'item1': df, 'item2': df}

In [124]: panel = pd.Panel.from_dict(data, orient='minor')

In [125]: panel['a']
Out[125]: 
  item1 item2
0   foo   foo
1   bar   bar
2   baz   baz

In [126]: panel['b']
Out[126]: 
      item1     item2
0 -1.142863 -1.142863
1 -1.015321 -1.015321
2  0.683625  0.683625

In [127]: panel['b'].dtypes
Out[127]: 
item1    float64
item2    float64
dtype: object

注意

Panel由于不如Series和DataFrame常用,所以它也不像它们那样拥有那么多的好用的功能。dataframe中许多方法和选项在panel中都不能适用。我们将会继续努力。

基于使用to_panel方法的dataframe的panel

在v0.7中介绍了用来代替LongPanel的这个方法,它能够将一个两层索引的DataFrame转换为一个panel。

 

In [128]: midx = pd.MultiIndex(levels=[['one', 'two'], ['x','y']], labels=[[1,1,0,0],[1,0,1,0]])

In [129]: df = pd.DataFrame({'A' : [1, 2, 3, 4], 'B': [5, 6, 7, 8]}, index=midx)

In [130]: df.to_panel()
Out[130]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 2 (minor_axis)
Items axis: A to B
Major_axis axis: one to two
Minor_axis axis: x to y

项的选取/增加/删除

和dataframe是多个Series组成的字典结构类似,panel就像是一个由多个dataframe组成的字典:

In [131]: wp['Item1']
Out[131]: 
                   A         B         C         D
2000-01-01 -0.729430  0.427693 -0.121325 -0.736418
2000-01-02  0.739037 -0.648805 -0.383057  0.385027
2000-01-03  2.321064 -1.290881  0.105458 -1.097035
2000-01-04  0.158759 -1.261191 -0.081710  1.390506
2000-01-05 -1.962031 -0.505580  0.021253 -0.317071

In [132]: wp['Item3'] = wp['Item1'] / wp['Item2']

Panel的插入和删除的接口和dataframe一样。对于dataframe来说,如果项是一个有效的Python识别器,你可以适用Tab自动完成来像访问属性一样访问它。

转置

可以适用转置方法改变一个panel(该方法默认不是生成一个副本,除非数据是异构的)

In [133]: wp.transpose(2, 0, 1)
Out[133]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 4 (items) x 3 (major_axis) x 5 (minor_axis)
Items axis: A to D
Major_axis axis: Item1 to Item3
Minor_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00

索引/选取

操作语法结果
选取项 wp[item] DataFrame
在主轴标签上切片 wp.major_xs(val) DataFrame
在次轴标签上切片 wp.minor_xs(val) DataFrame

例如,使用之前的示例数据,我们可以:

In [134]: wp['Item1']
Out[134]: 
                   A         B         C         D
2000-01-01 -0.729430  0.427693 -0.121325 -0.736418
2000-01-02  0.739037 -0.648805 -0.383057  0.385027
2000-01-03  2.321064 -1.290881  0.105458 -1.097035
2000-01-04  0.158759 -1.261191 -0.081710  1.390506
2000-01-05 -1.962031 -0.505580  0.021253 -0.317071

In [135]: wp.major_xs(wp.major_axis[2])
Out[135]: 
      Item1     Item2     Item3
A  2.321064 -0.538606 -4.309389
B -1.290881  0.791512 -1.630905
C  0.105458 -0.020302 -5.194337
D -1.097035  0.184430 -5.948253

In [136]: wp.minor_axis
Out[136]: Index([u'A', u'B', u'C', u'D'], dtype='object')

In [137]: wp.minor_xs('C')
Out[137]: 
               Item1     Item2     Item3
2000-01-01 -0.121325  1.413524 -0.085832
2000-01-02 -0.383057  1.243178 -0.308127
2000-01-03  0.105458 -0.020302 -5.194337
2000-01-04 -0.081710 -1.811565  0.045105
2000-01-05  0.021253 -1.040542 -0.020425

Squeeze

改变一个对象维度的另一个方法是squeeze一个1-len对象,类似于wp['Item1]

In [138]: wp.reindex(items=['Item1']).squeeze()
Out[138]: 
                   A         B         C         D
2000-01-01 -0.729430  0.427693 -0.121325 -0.736418
2000-01-02  0.739037 -0.648805 -0.383057  0.385027
2000-01-03  2.321064 -1.290881  0.105458 -1.097035
2000-01-04  0.158759 -1.261191 -0.081710  1.390506
2000-01-05 -1.962031 -0.505580  0.021253 -0.317071

In [139]: wp.reindex(items=['Item1'], minor=['B']).squeeze()
Out[139]: 
2000-01-01    0.427693
2000-01-02   -0.648805
2000-01-03   -1.290881
2000-01-04   -1.261191
2000-01-05   -0.505580
Freq: D, Name: B, dtype: float64

转换为DataFrame

一个panel能够被一个分层索引的dataframe二维表格替代。详见hierarchical indexing。使用to_frame方法将一个panel转换为一个dataframe。

In [140]: panel = pd.Panel(np.random.randn(3, 5, 4), items=['one', 'two', 'three'],
   .....:                  major_axis=pd.date_range('1/1/2000', periods=5),
   .....:                  minor_axis=['a', 'b', 'c', 'd'])
   .....: 

In [141]: panel.to_frame()
Out[141]: 
                       one       two     three
major      minor                              
2000-01-01 a     -1.876826 -0.383171 -0.117339
           b     -1.873827 -0.172217  0.780048
           c     -0.251457 -1.674685  2.162047
           d      0.027599  0.762474  0.874233
2000-01-02 a      1.235291  0.481666 -0.764147
           b      0.850574  1.217546 -0.484495
           c     -1.140302  0.577103  0.298570
           d      2.149143 -0.076021  0.825136
2000-01-03 a      0.504452  0.720235 -0.388020
           b      0.678026  0.202660 -0.339279
           c     -0.628443 -0.314950  0.141164
           d      1.191156 -0.410852  0.565930
2000-01-04 a     -1.145363  0.542758 -1.749969
           b     -0.523153  1.955407 -1.402941
           c     -1.299878 -0.940645  0.623222
           d     -0.110240  0.076257  0.020129
2000-01-05 a     -0.333712 -0.897159 -2.858463
           b      0.416876 -1.265679  0.885765
           c     -0.436400 -0.528311  0.158014
           d      0.999768 -0.660014 -1.981797

四维Panel 和多维Panel (不推荐使用)

警告

在0.19.0版本中不推荐使用四维Panel和多维panel,它们将会在以后的版本中被逐渐移除。推荐的使用方式是使用 xarray package来代替这种多维数据。Pandas提供了一个to_xarray()方法来自动完成此转换。

详见 docs of a previous version 。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2017-01-11 15:53  shuhanrainbow  阅读(462)  评论(0编辑  收藏  举报