Python 数据处理 —— pandas 数据结构

前言

对于如何使用 R 来处理表格数据,相信你已经学习到很多了。

下面我们的重点是介绍如何在 Python 中处理表格型数据,提到 Python 数据处理,那就不得不说 pandas 了。

pandasPython 数据处理的核心库,提供了快速、灵活、明确的数据结构,能够简单直接地处理结构性和关系型数据。

panda 适用于很多不同类型的数据:

  • 具有不同类型的列的表格数据,如 ExcelSQL
  • 有序和无序(不一定是固定频率)的时间序列数据
  • 具有行和列标签的任意矩阵数据(同类型或异构数据)
  • 任何其他形式的观察/统计数据集。数据实际上根本不需要标记就可以放置到 pandas 数据结构中

Pandas 主要的数据结构是 Series(一维)与 DataFrame(二维),使用这两种数据结构就足以应对金融、统计、社会科学、工程等领域里的大多数数据了。

相较于 R 语言的 data.frameDataFrame 提供了更加丰富的功能。

Pandas 是基于 NumPy 开发的,可以与其它第三方科学计算库完美集成。

能够熟练使用 pandas,处理数据将是事半功倍的,它的种种优点就不一一介绍了,下面开始正题吧。

安装

如果你系统中安装的是 Anaconda 环境的话,会自动安装 pandasnumpymatplotlib 等常用的数据科学相关的库。

如果没有使用 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.23Series 会按字典的插入顺序对索引排序。

在其他版本中 Series 会按字典的键(key)的字母顺序排序列表。

也就是说,如果我们的 Python < 3.6Pandas < 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 转换为 arraynumpy 数组

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

可以使用成员属性 indexcolumns 访问索引和列名

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 将会有同样的索引,如果未设置列名,默认将使用 Seriesname

>>> 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
用方法链分配新列

dplyrmutate 动词的启发,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'>

image.png

在这个例子中,我们使用了 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
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这篇笔记主要介绍了Pandas模块的基本操作和使用方法。PandasPython中一个用于数据分析和处理的常用库,提供了高效的数据结构和数据分析工具,是进行数据处理和数据挖掘的重要工具之一。 一、Pandas数据结构 Pandas主要有两种数据结构:Series和DataFrame。 1. Series Series是一种类似于一维数组的对象,由一组数据和一组与之相关的标签(即索引)组成。Series的创建方式如下: ```python import pandas as pd # 通过列表创建Series s = pd.Series([1, 3, 5, np.nan, 6, 8]) # 通过字典创建Series s = pd.Series({'a': 1, 'b': 2, 'c': 3}) ``` 2. DataFrame DataFrame是一种二维表格数据结构,由一组数据和一组行索引和列索引组成。DataFrame的创建方式有很多种,最常用的是通过字典创建。例如: ```python import pandas as pd data = {'name': ['Tom', 'Jerry', 'Mike'], 'age': [18, 20, 22], 'gender': ['M', 'M', 'F']} df = pd.DataFrame(data) ``` 二、Pandas的基本操作 1. 数据读取 Pandas可以读取多种格式的数据文件,如CSV、Excel、SQL等。常用的读取CSV文件的方式如下: ```python import pandas as pd df = pd.read_csv('data.csv') ``` 2. 数据预处理 数据预处理是数据挖掘中非常重要的一部分,Pandas提供了很多方便的函数和方法来进行数据清洗和转换。常用的数据预处理函数和方法有: - 处理缺失值 ```python # 判断是否存在缺失值 df.isnull() # 删除缺失值 df.dropna() # 填充缺失值 df.fillna(value) ``` - 处理重复值 ```python # 删除重复值 df.drop_duplicates() ``` - 数据转换 ```python # 数据类型转换 df.astype() # 数据替换 df.replace() ``` 3. 数据分析 Pandas提供了各种数据分析和处理的方法和函数,常用的包括: - 统计函数 ```python # 计算平均值 df.mean() # 计算标准差 df.std() # 计算最大值和最小值 df.max(), df.min() ``` - 排序 ```python # 按照某列排序 df.sort_values(by='column_name') ``` - 数据聚合 ```python # 对某列数据进行分组求和 df.groupby('column_name').sum() ``` 以上是Pandas模块的基础内容,还有很多高级用法和技巧需要进一步学习和掌握。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

名本无名

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

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

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

打赏作者

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

抵扣说明:

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

余额充值