前言
对于如何使用 R
来处理表格数据,相信你已经学习到很多了。
下面我们的重点是介绍如何在 Python
中处理表格型数据,提到 Python
数据处理,那就不得不说 pandas
了。
pandas
是 Python
数据处理的核心库,提供了快速、灵活、明确的数据结构,能够简单直接地处理结构性和关系型数据。
panda
适用于很多不同类型的数据:
- 具有不同类型的列的表格数据,如
Excel
和SQL
表 - 有序和无序(不一定是固定频率)的时间序列数据
- 具有行和列标签的任意矩阵数据(同类型或异构数据)
- 任何其他形式的观察/统计数据集。数据实际上根本不需要标记就可以放置到
pandas
数据结构中
Pandas
主要的数据结构是 Series
(一维)与 DataFrame
(二维),使用这两种数据结构就足以应对金融、统计、社会科学、工程等领域里的大多数数据了。
相较于 R
语言的 data.frame
,DataFrame
提供了更加丰富的功能。
Pandas
是基于 NumPy
开发的,可以与其它第三方科学计算库完美集成。
能够熟练使用 pandas
,处理数据将是事半功倍的,它的种种优点就不一一介绍了,下面开始正题吧。
安装
如果你系统中安装的是 Anaconda
环境的话,会自动安装 pandas
、numpy
、matplotlib
等常用的数据科学相关的库。
如果没有使用 Anaconda
的话可以使用
pip install pandas
一般我是推荐使用 Anaconda
环境的,是真的方便。还不知道如何安装配置 Anaconda
的话,可以找到我前面的文章,里面有详细的讲解。
数据结构
本节,我们先开始介绍 pandas
的基础数据结构
当然啦,使用这个包肯定得先导入了,同时我们也会使用到 numpy
中的一些函数,标准的导入方式是
In [1]: import numpy as np
In [2]: import pandas as pd
1. Series
Series
是带标签的一维数组,可以存储任意数据类型,如整数、浮点数、字符串、Python
对象等类型的数据。轴标签称为索引(index
),可以使用 pd.Series
函数来创建
>>> s = pd.Series(data, index=index)
其中,data
可以是
python
字典- 多维数组
- 标量值(如
5
)
index
是对应的标签列表。根据不同的数据类型,分为以下几种情况:
多维数组
当 data
是多维数组时,index
长度必须与 data
长度一致。如果没有指定 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.469112
b -0.282863
c -1.509059
d -1.135632
e 1.212112
dtype: float64
In [5]: s.index
Out[5]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
In [6]: pd.Series(np.random.randn(5))
Out[6]:
0 -0.173215
1 0.119209
2 -1.044236
3 -0.861849
4 -2.104569
dtype: float64
注意:Pandas
的索引值是可以重复,如果使用不支持重复索引的操作会触发异常。
In [7]: pd.Series(np.random.randn(5), index=['a', 'b', 'd', 'd', 'e'])
Out[7]:
a 0.624372
b 0.173827
d 0.078082
d 0.765597
e 0.631827
dtype: float64
字典
可以使用字典实例化 Series
:
In [8]: d = {'b': 1, 'a': 0, 'c': 2}
In [9]: pd.Series(d)
Out[9]:
b 1
a 0
c 2
dtype: int64
注意:当 data
为字典,且未设置 index
参数时,如果 Python
的版本 >= 3.6 且 Pandas
的版本 >= 0.23
,Series
会按字典的插入顺序对索引排序。
在其他版本中 Series
会按字典的键(key
)的字母顺序排序列表。
也就是说,如果我们的 Python < 3.6
或 Pandas < 0.23
,上面的结果将是
a 0
b 1
c 2
dtype: int64
如果设置了 index
参数,会将 data
的数据根据 index
的顺序排序显示,不存在的值会复制为 NaN
In [10]: d = {'a': 0., 'b': 1., 'c': 2.}
In [11]: pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[11]:
b 1.0
c 2.0
d NaN
a 0.0
dtype: float64
注意:在 pandas
中使用 NaN(Not a Number)
表示缺失值
标量值
如果 data
是标量值,那么必须设置索引值。Series
会按索引的长度重复该标量值。
In [12]: pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
Out[12]:
a 5.0
b 5.0
c 5.0
d 5.0
e 5.0
dtype: float64
Series 类似 numpy 的 ndarray
Series
的行为与 ndarray
类似,支持大部分的 NumPy
函数,同时还支持索引切片。
In [13]: s[0]
Out[13]: -1.9744628992708957
In [14]: s[:3]
Out[14]:
a -1.974463
b 1.926031
c 0.659861
dtype: float64
In [15]: s[s > s.median()]
Out[15]:
b 1.926031
e 1.077661
dtype: float64
In [16]: s[[4, 3, 1]]
Out[16]:
e 1.077661
d -1.386355
b 1.926031
dtype: float64
In [17]: np.exp(s)
Out[17]:
a 0.138836
b 6.862223
c 1.934524
d 0.249985
e 2.937800
dtype: float64
注意:我们可以使用 s[[4, 3, 1]]
这中传递列表的方式提取指定位置的值,还可以是重复的值,会重复提取
>>> s[[4, 3, 1, 1]]
e 1.077661
d -1.386355
b 1.926031
b 1.926031
dtype: float64
我们可以使用 dtype
属性获取数据的类型
In [18]: s.dtype
Out[18]: dtype('float64')
使用 astype()
设置数据的类型
In [19]: s.astype(str)
Out[19]:
a -1.9744628992708957
b 1.9260314025924432
c 0.6598612596804069
d -1.3863546449807986
e 1.0776610911873974
dtype: object
我们可以将 Series
转换为 array
或 numpy
数组
In [20]: s.array
Out[20]:
<PandasArray>
[-1.9744628992708957, 1.9260314025924432, 0.6598612596804069,
-1.3863546449807986, 1.0776610911873974]
Length: 5, dtype: float64
In [21]: s.to_numpy()
Out[21]: array([-1.9744629 , 1.9260314 , 0.65986126, -1.38635464, 1.07766109])
Series 也类似字典
Series
类似字典,可以用索引标签提取值或设置对应的值
In [22]: s['b']
Out[22]: 1.9260314025924432
In [23]: s['e'] = 12.
In [24]: s
Out[24]:
a -1.974463
b 1.926031
c 0.600000
d -1.386355
e 12.000000
dtype: float64
In [25]: 'e' in s
Out[25]: True
In [26]: 'f' in s
Out[26]: False
如果想要访问的标签不存在,会引发错误
In [27]: s[f]
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-28-c86c4a9a4a10> in <module>
----> 1 s[f]
NameError: name 'f' is not defined
也可以使用 get
方法提取 Series
里标签对应的值,当标签不存在时返回 None
或指定其他值
In [28]: s.get('f')
In [29]: s.get('f', np.nan)
Out[29]: nan
Series 支持矢量操作
Series
支持大多数数组操作
In [30]: s + s
Out[30]:
a -3.948926
b 3.852063
c 1.200000
d -2.772709
e 24.000000
dtype: float64
In [31]: s * 2
Out[31]:
a -3.948926
b 3.852063
c 1.200000
d -2.772709
e 24.000000
dtype: float64
Series
之间的操作会自动对齐标签数据,因此,不用顾及执行计算操作的 Series
是否有相同的标签。
In [32]: s[1:] + s[:-1]
Out[32]:
a NaN
b 3.852063
c 1.200000
d -2.772709
e NaN
dtype: float64
无法对应上的标签的运算结果会标记为 NaN
,计算结果会是所有涉及到的标签的并集
这样我们就无需编写代码显式对齐数据,为数据分析和研究提供了巨大的自由度和灵活性
注意:默认让不同索引对象操作的结果生成索引并集,是为了避免信息的丢失,当然我们也可以使用 drop
方法删除包含缺失值的标签。
名称属性
Series
支持 name
属性:
In [33]: s = pd.Series(np.random.randn(5), name='something')
In [34]: s
Out[34]:
0 1.926419
1 1.175251
2 -0.568534
3 -0.014069
4 1.401082
Name: something, dtype: float64
In [35]: s.name
Out[35]: 'something'
一般情况下,Series
自动分配 name
属性,特别是从 DataFrame
提取一维数据切片时。
可以使用 rename
方法对 Series
重命名
In [36]: s2 = s.rename("different")
In [37]: s2.name
Out[37]: 'different'
2. DataFrame
DataFrame
数二维带标签的数据结构,每列可以是不同的数据类型,你可以把它想象成一个 excel
表格或 SQL
表,或者是由 Series 对象构成的字典。
DataFrame
是我们最常用的对象,它也支持多种类型的输入数据
- 一维数组字典,列表、字典或
Series
- 二维数组
- 结构化数组或记录数组
Series
- 其他
DataFrame
除了传入数据,也可以同时指定数据的索引(index
)和列名(columns
)。如果输入数据是字典类型的 Series
且带有索引,那么会将与指定索引不匹配的数据删除
如果没有设置轴标签,则按常规数据解析构建索引和列名
注意:与前面提到的从字典构建 Series
对象类似,在 Python > = 3.6
,且 Pandas > = 0.23
时,当输入数据是字典,且未指定 columns
参数时,DataFrame
的列按字典的插入顺序排序。其他版本会按照字典键的字母顺序对列排序
值为 Series 的字典
用 Series
字典构建 DataFrame
其生成的索引是各个 Series
索引的并集。如果存在嵌套的字典,会先把嵌套字典转换为 Series
。如果未指定列名,会将字典的键作为列名
In [37]: d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
...: 'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])
...: }
In [38]: df = pd.DataFrame(d)
In [39]: df
Out[40]:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In [40]: pd.DataFrame(d, index=["d", "b", "a"])
Out[40]:
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
In [41]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[41]:
two three
d 4.0 NaN
b 2.0 NaN
a 1.0 NaN
可以使用成员属性 index
和 columns
访问索引和列名
In [42]: df.index
Out[42]: Index(['a', 'b', 'c', 'd'], dtype='object')
In [43]: df.columns
Out[43]: Index(['one', 'two'], dtype='object')
注意:如果设置了列名参数,会覆盖原本字典的键
值为数组/列表的字典
字典里的每个数组长度必须一致,如果指定了索引参数,那么索引的长度必须与数组长度一致。如果未指定索引,默认会将 range(n)
作为索引,n
为数组长度
In [44]: d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}
In [45]: pd.DataFrame(d)
Out[45]:
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
In [46]: pd.DataFrame(d, index=["a", "b", "c", "d"])
Out[46]:
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
如果长度不一致,会抛出异常
>>>pd.DataFrame(d, index=["a", "b", "c"])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
结构化数组和记录型数组
这种情况的处理方式与值为数组的字典一样
In [47]: data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])
In [48]: data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]
In [49]: pd.DataFrame(data)
Out[49]:
A B C
0 1 2.0 b'Hello'
1 2 3.0 b'World'
In [50]: pd.DataFrame(data, index=["first", "second"])
Out[50]:
A B C
first 1 2.0 b'Hello'
second 2 3.0 b'World'
In [51]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[51]:
C A B
0 b'Hello' 1 2.0
1 b'World' 2 3.0
注意:DataFrame
的运作方式与二维 NumPy
数组是不一样的
字典列表
In [52]: data = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
In [53]: pd.DataFrame(data)
Out[53]:
a b c
0 1 2 NaN
1 5 10 20.0
In [54]: pd.DataFrame(data, index=["first", "second"])
Out[54]:
a b c
first 1 2 NaN
second 5 10 20.0
In [55]: pd.DataFrame(data, columns=["a", "b"])
Out[55]:
a b
0 1 2
1 5 10
元组型字典
通过传递元组字典,可以自动创建多级索引
In [56]: 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[56]:
a b
b a c a b
A B 1.0 4.0 5.0 8.0 10.0
C 2.0 3.0 6.0 7.0 NaN
D NaN NaN NaN NaN 9.0
Series
从 Series
构建的 DataFrame
将会有同样的索引,如果未设置列名,默认将使用 Series
的 name
>>> s = pd.Series(np.random.randn(5), index=list('abcde'), name='something')
>>> s
a -0.722865
b 1.317593
c -1.843862
d -1.530197
e 0.404915
Name: something, dtype: float64
>>> pd.DataFrame(s)
something
a -0.722865
b 1.317593
c -1.843862
d -1.530197
e 0.404915
命名元组列表
DataFrame
的列数由列表中第一个 namedtuple
的字段名称确定,其余的命名元组(或元组)会被简单地解压缩,并填到新的行中
如果其中任何一个元组比第一个命名的元组短,则相应的行中后面的列将标记为缺失值。如果存在长度超过第一个命名元组的元组,则会引发 ValueError
。
In [57]: from collections import namedtuple
In [58]: Point = namedtuple("Point", "x y")
In [59]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[59]:
x y
0 0 0
1 0 3
2 2 3
In [60]: Point3D = namedtuple("Point3D", "x y z")
In [61]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[61]:
x y z
0 0 0 0.0
1 0 3 5.0
2 2 3 NaN
备选构造函数
DataFrame.from_dict
DataFrame.from_dict
接收嵌套字典或数组序列的字典,并生成 DataFrame
。
其中 orient
参数默认为 columns
,该构建器的操作与 DataFrame
构建器类似。
In [65]: pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
Out[65]:
A B
0 1 4
1 2 5
2 3 6
如果 orient
参数设置为 index
, 则会把字典的键作为行索引,同时还可以设置列名。
In [66]: pd.DataFrame.from_dict(
....: dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
....: orient="index",
....: columns=["one", "two", "three"],
....: )
....:
Out[66]:
one two three
A 1 2 3
B 4 5 6
DataFrame.from_records
DataFrame.from_records
接受元组列表或结构数据类型(dtype
)的多维数组。
与 DataFrame
构建器类似,只不过生成的 DataFrame
索引是结构数据类型指定的字段。例如
In [67]: data
Out[67]:
array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In [68]: pd.DataFrame.from_records(data, index="C")
Out[68]:
A B
C
b'Hello' 1 2.0
b'World' 2 3.0
列的选择、添加、删除
你可以在语义上将 DataFrame
看作是类似 Series
的字典,获取、设置和删除列的语法与字典类似
In [69]: df["one"]
Out[69]:
a 1.0
b 2.0
c 3.0
d NaN
Name: one, dtype: float64
In [70]: df["three"] = df["one"] * df["two"]
In [71]: df["flag"] = df["one"] > 2
In [72]: df
Out[72]:
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
可以像字典一样删除或 pop
列
In [73]: del df["two"]
In [74]: three = df.pop("three")
In [75]: df
Out[75]:
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
当插入标量值时,它会自动扩展来填充列
In [76]: df["foo"] = "bar"
In [77]: df
Out[77]:
one flag foo
a 1.0 False bar
b 2.0 False bar
c 3.0 True bar
d NaN False bar
当插入与 DataFrame
索引不同的 Series
时,它将与 DataFrame
的索引对齐,未匹配的赋值为 NaN
In [78]: df["one_trunc"] = df["one"][:2]
In [79]: df
Out[79]:
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
您可以插入原始的 ndarray
数组,但是它们的长度必须与 DataFrame
索引的长度一致
默认情况下,列会插入到末尾。 使用 insert
函数可用于在列中的特定位置插入
In [80]: df.insert(1, "bar", df["one"])
In [81]: df
Out[81]:
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
的 mutate
动词的启发,DataFrame
也提供了一个 assign
方法,利用现有的列创建新的列
In [82]: iris = pd.read_csv("data/iris.data")
In [83]: iris.head()
Out[83]:
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 [84]: iris.assign(sepal_ratio=iris["SepalWidth"] / iris["SepalLength"]).head()
Out[84]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
还可以传递带参数的函数进行求值
In [85]: iris.assign(sepal_ratio=lambda x: (x["SepalWidth"] / x["SepalLength"])).head()
Out[85]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
assign
返回的是一个数据拷贝,而不会修改原数据
当你在数据操作链的中间想要添加一列数据,又不想写入数据中时,会非常有用。
例如,对于花萼长度大于 5
的数据,计算比例并绘制图像
In [86]: (
....: 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[86]: <AxesSubplot:xlabel='SepalRatio', ylabel='PetalRatio'>
在这个例子中,我们使用了 DataFrame
的链式操作,先筛选出花萼长度大于 5
的数据,然后用 assign
计算并添加两列比例值,最后绘制这两列计算值的散点图
从 3.6
版开始,Python
可以保存 **kwargs
顺序。也就是说前面的参数会保存,可以让后面的参数调用。
In [87]: dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
In [88]: dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
Out[88]:
A B C D
0 1 4 5 6
1 2 5 7 9
2 3 6 9 12
在这个例子中,在新建 D
列的时候我们调用了之前创建的 C
列。即 D = A + (A + B)
要兼容所有的版本,可以使用链式操作
>>> dfa.assign(C=lambda x: x["A"] + x["B"]).assign(D=lambda x: x["A"] + x["C"])
A B C D
0 1 4 5 6
1 2 5 7 9
2 3 6 9 12
3. DataFrame(续)
索引和选择
索引的基础语法如下
操作 | 语法 | 结果 |
---|---|---|
选择列 | df[col] | Series |
用标签选择行 | df.loc[label] | Series |
用整数位置选择行 | df.iloc[loc] | Series |
用布尔向量选择行 | df[bool_vec] | DataFrame |
行切片 | df[5:10] | DataFrame |
例如,选择行返回的是 Series,其索引是 DataFrame 的列名:
In [89]: df.loc["b"]
Out[89]:
one 2.0
bar 2.0
flag False
foo bar
one_trunc 2.0
Name: b, dtype: object
In [90]: df.iloc[2]
Out[90]:
one 3.0
bar 3.0
flag True
foo bar
one_trunc NaN
Name: c, dtype: object
关于索引切片的详细内容,我们将会在后续的索引章节详细介绍
数据对齐和运算
DataFrame 对象之间的数据会根据索引和列名自动对齐,结果将是索引和列名的并集
In [91]: df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])
In [92]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])
In [93]: df + df2
Out[93]:
A B C D
0 0.045691 -0.014138 1.380871 NaN
1 -0.955398 -1.501007 0.037181 NaN
2 -0.662690 1.534833 -0.859691 NaN
3 -2.452949 1.237274 -0.133712 NaN
4 1.414490 1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
DataFrame 和 Series 之间执行操作时,默认行为是 DataFrame 的列名与 Series 的索引对齐,然后按行执行广播操作。例如
In [94]: df - df.iloc[0]
Out[94]:
A B C D
0 0.000000 0.000000 0.000000 0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2 0.253128 0.829678 0.010026 -1.991234
3 -1.311128 0.054325 -1.724913 -1.620544
4 0.573025 1.500742 -0.676070 1.367331
5 -1.741248 0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282 0.000430
7 -0.743894 0.411013 -0.929563 -0.282386
8 -1.194921 1.320690 0.238224 -1.482644
9 2.293786 1.856228 0.773289 -1.446531
那如果使用的是列会发生什么
>>> df
A B C
0 1 3 4
1 2 5 0
2 3 1 1
3 4 7 6
4 5 2 2
>>> df - df['A']
A B C 0 1 2 3 4
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN
因为我们提取的 A 列的索引是 0-4,与 df 的列名 A、B、C 不匹配,最后导致结果都为 NaN
标量操作与其它数据结构是一样的
In [95]: df * 5 + 2
Out[95]:
A B C D
0 3.359299 -0.124862 4.835102 3.381160
1 -3.437003 -1.368449 2.568242 -5.392133
2 4.624938 4.023526 4.885230 -6.575010
3 -3.196342 0.146766 -3.789461 -4.721559
4 6.224426 7.378849 1.454750 10.217815
5 -5.346940 3.785103 -1.373001 -6.884519
6 -2.844569 -4.472618 4.068691 3.383309
7 -0.360173 1.930201 0.187285 1.969232
8 -2.615303 6.478587 6.026220 -4.032059
9 14.828230 9.156280 8.701544 -3.851494
In [96]: 1 / df
Out[96]:
A B C D
0 3.678365 -2.353094 1.763605 3.620145
1 -0.919624 -1.484363 8.799067 -0.676395
2 1.904807 2.470934 1.732964 -0.583090
3 -0.962215 -2.697986 -0.863638 -0.743875
4 1.183593 0.929567 -9.170108 0.608434
5 -0.680555 2.800959 -1.482360 -0.562777
6 -1.032084 -0.772485 2.416988 3.614523
7 -2.118489 -71.634509 -2.758294 -162.507295
8 -1.083352 1.116424 1.241860 -0.828904
9 0.389765 0.698687 0.746097 -0.854483
In [97]: df ** 4
Out[97]:
A B C D
0 0.005462 3.261689e-02 0.103370 5.822320e-03
1 1.398165 2.059869e-01 0.000167 4.777482e+00
2 0.075962 2.682596e-02 0.110877 8.650845e+00
3 1.166571 1.887302e-02 1.797515 3.265879e+00
4 0.509555 1.339298e+00 0.000141 7.297019e+00
5 4.661717 1.624699e-02 0.207103 9.969092e+00
6 0.881334 2.808277e+00 0.029302 5.858632e-03
7 0.049647 3.797614e-08 0.017276 1.433866e-09
8 0.725974 6.437005e-01 0.420446 2.118275e+00
9 43.329821 4.196326e+00 3.227153 1.875802e+00
对于布尔运算同样适用
In [98]: df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)
In [99]: df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)
In [100]: df1 & df2
Out[100]:
a b
0 False False
1 False True
2 True False
In [101]: df1 | df2
Out[101]:
a b
0 True True
1 True True
2 True True
In [102]: df1 ^ df2
Out[102]:
a b
0 True True
1 True False
2 False True
In [103]: -df1
Out[103]:
a b
0 False True
1 True False
转置
与多位数组类似,可以对 DataFrame 转置,使用 T 属性或 transpose 函数
In [104]: df[:5].T
Out[104]:
0 1 2 3 4
A 0.271860 -1.087401 0.524988 -1.039268 0.844885
B -0.424972 -0.673690 0.404705 -0.370647 1.075770
C 0.567020 0.113648 0.577046 -1.157892 -0.109050
D 0.276232 -1.478427 -1.715002 -1.344312 1.643563
应用 numpy 函数
如果你的 DataFrame 存储的都是数字,可以使用许多 NumPy 的函数
In [105]: np.exp(df)
Out[105]:
A B C D
0 1.312403 0.653788 1.763006 1.318154
1 0.337092 0.509824 1.120358 0.227996
2 1.690438 1.498861 1.780770 0.179963
3 0.353713 0.690288 0.314148 0.260719
4 2.327710 2.932249 0.896686 5.173571
5 0.230066 1.429065 0.509360 0.169161
6 0.379495 0.274028 1.512461 1.318720
7 0.623732 0.986137 0.695904 0.993865
8 0.397301 2.449092 2.237242 0.299269
9 13.009059 4.183951 3.820223 0.310274
In [106]: np.asarray(df)
Out[106]:
array([[ 0.2719, -0.425 , 0.567 , 0.2762],
[-1.0874, -0.6737, 0.1136, -1.4784],
[ 0.525 , 0.4047, 0.577 , -1.715 ],
[-1.0393, -0.3706, -1.1579, -1.3443],
[ 0.8449, 1.0758, -0.109 , 1.6436],
[-1.4694, 0.357 , -0.6746, -1.7769],
[-0.9689, -1.2945, 0.4137, 0.2767],
[-0.472 , -0.014 , -0.3625, -0.0062],
[-0.9231, 0.8957, 0.8052, -1.2064],
[ 2.5656, 1.4313, 1.3403, -1.1703]])
如果在 NumPy 通用函数中使用了多个 Series,会在执行函数之前,自动对齐。
例如
In [109]: ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
In [110]: ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])
In [111]: ser1
Out[111]:
a 1
b 2
c 3
dtype: int64
In [112]: ser2
Out[112]:
b 1
a 3
c 5
dtype: int64
In [113]: np.remainder(ser1, ser2)
Out[113]:
a 1
b 0
c 3
dtype: int64
如果存在对应不上的索引,会被赋值为 NaN
In [114]: ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])
In [115]: ser3
Out[115]:
b 2
c 4
d 6
dtype: int64
In [116]: np.remainder(ser1, ser3)
Out[116]:
a NaN
b 0.0
c 3.0
d NaN
dtype: float64
如果在 Series 和 index 上应用二元函数时,会按照 Series 执行并输出
In [117]: ser = pd.Series([1, 2, 3])
In [118]: idx = pd.Index([4, 5, 6])
In [119]: np.maximum(ser, idx)
Out[119]:
0 4
1 5
2 6
dtype: int64
控制台显示
在控制台显示大型数据时,会根据数据量进行折叠展示前面和后面的几行
In [120]: baseball = pd.read_csv("data/baseball.csv")
In [121]: print(baseball)
id player year stint team lg g ab r h ... rbi sb cs bb so ibb hbp sh sf gidp
0 88641 womacto01 2006 2 CHN NL 19 50 6 14 ... 2.0 1.0 1.0 4 4.0 0.0 0.0 3.0 0.0 0.0
1 88643 schilcu01 2006 1 BOS AL 31 2 0 1 ... 0.0 0.0 0.0 0 1.0 0.0 0.0 0.0 0.0 0.0
.. ... ... ... ... ... .. .. ... .. ... ... ... ... ... .. ... ... ... ... ... ...
98 89533 aloumo01 2007 1 NYN NL 87 328 51 112 ... 49.0 3.0 0.0 27 30.0 5.0 2.0 0.0 3.0 13.0
99 89534 alomasa02 2007 1 NYN NL 8 22 1 3 ... 0.0 0.0 0.0 0 3.0 0.0 0.0 0.0 0.0 0.0
[100 rows x 23 columns]
可以使用 info 函数显示汇总信息
In [122]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 100 non-null int64
1 player 100 non-null object
2 year 100 non-null int64
3 stint 100 non-null int64
4 team 100 non-null object
5 lg 100 non-null object
6 g 100 non-null int64
7 ab 100 non-null int64
8 r 100 non-null int64
9 h 100 non-null int64
10 X2b 100 non-null int64
11 X3b 100 non-null int64
12 hr 100 non-null int64
13 rbi 100 non-null float64
14 sb 100 non-null float64
15 cs 100 non-null float64
16 bb 100 non-null int64
17 so 100 non-null float64
18 ibb 100 non-null float64
19 hbp 100 non-null float64
20 sh 100 non-null float64
21 sf 100 non-null float64
22 gidp 100 non-null float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.1+ KB
默认情况下,过宽的数据会换行打印,可以设置列宽 display.width 来控制
In [123]: pd.set_option("display.width", 40) # default is 80
In [124]: pd.DataFrame(np.random.randn(3, 12))
Out[124]:
0 1 2 3 4 ... 7 8 9 10 11
0 -2.182937 0.380396 0.084844 0.432390 1.519970 ... 0.274230 0.132885 -0.023688 2.410179 1.450520
1 0.206053 -0.251905 -2.213588 1.063327 1.266143 ... 0.408204 -1.048089 -0.025747 -0.988387 0.094055
2 1.262731 1.289997 0.082423 -0.055758 0.536580 ... -0.034571 -2.484478 -0.281461 0.030711 0.109121
[3 rows x 12 columns]
还可以设置最大列宽 display.max_colwidth 来控制
In [125]: datafile = {
.....: "filename": ["filename_01", "filename_02"],
.....: "path": [
.....: "media/user_name/storage/folder_01/filename_01",
.....: "media/user_name/storage/folder_02/filename_02",
.....: ],
.....: }
.....:
In [126]: pd.set_option("display.max_colwidth", 30)
In [127]: pd.DataFrame(datafile)
Out[127]:
filename path
0 filename_01 media/user_name/storage/fo...
1 filename_02 media/user_name/storage/fo...
In [128]: pd.set_option("display.max_colwidth", 100)
In [129]: pd.DataFrame(datafile)
Out[129]:
filename path
0 filename_01 media/user_name/storage/folder_01/filename_01
1 filename_02 media/user_name/storage/folder_02/filename_02
DataFrame 列属性
如果 DataFrame 的列名是有效的 Python 变量名时,可以通过访问对象属性的方式提取对应的列
In [130]: df = pd.DataFrame({'foo1': np.random.randn(5),
.....: 'foo2': np.random.randn(5)})
.....:
In [131]: df
Out[131]:
foo1 foo2
0 1.171216 -0.858447
1 0.520260 0.306996
2 -1.197071 -0.028665
3 -1.066969 0.384316
4 -0.303421 1.574159
In [132]: df.foo1
Out[132]:
0 1.171216
1 0.520260
2 -1.197071
3 -1.066969
4 -0.303421
Name: foo1, dtype: float64