烦恼一般都是想太多了。

0%

pandas中类似数据库的操作

之前就了解过相关的信息,不过在使用中还是不太熟悉,经常会卡住需要看文档,因此,在此再次来熟悉一下,方便记录,然后使用的时候也能更快的进行查询。
其本质,是对一个二维数据结构 DataFrame 的操作。
Pandas 提供了非常容易的进行 Series 和 DataFrame 结合,同时在其他的轴上设置一些相关的逻辑的特性。

Concatenating 对象

concat() 函数,提供了最重要的功能,比如一个简单的例子:

df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index=[0, 1, 2, 3])
df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7']},
index=[4, 5, 6, 7])
df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
'B': ['B8', 'B9', 'B10', 'B11'],
'C': ['C8', 'C9', 'C10', 'C11'],
'D': ['D8', 'D9', 'D10', 'D11']},
index=[8, 9, 10, 11])
frames = [df1, df2, df3]
result = pd.concat(frames)

输出的结果,是三个 DataFrame 沿列方向进行了拼接,但是这里面还有更多的细节值得讨论。

函数签名

pd.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,
levels=None, names=None, verify_integrity=False, copy=True)

其中:

  • objs : 一个 Serise 或 DataFrame 的序列,也可与 字典。如果传递的是一个字典,那么排序后的键将会作为此函数中的 keys 参数(在 keys参数没有传递的情况下)
  • axis : {0, 1, …}, 默认 0. 沿那个方向进行连接
  • join : {‘inner’, ‘outer’}, 默认 ‘outer’. 在其他轴上,如何操作索引。outer 进行 联合(union)操作,inner 进行交叉选择。
  • ignore_index : boolean, 默认 False. 如果是 True, 则不使用在连接轴上的索引值。结果的轴将会用标签 0, …, n - 1. 这在我们连接的对象在连接轴上没有有意义的索引时有用。注意,在其他轴上的索引仍然会被 join 操作所使用。
  • keys : 序列, 默认 None. 会构建有层级的索引,而 keys 传递的作为最外层的索引。如果传递了多个级别,那么应该使用 元组 .
  • levels : 序列的列表, 默认 None.指定了用来构建 多级索引 的级别(唯一值) 。如果不指定的话,这个值将会从 keys 进行推断。
  • names : 列表, 默认 None.在结果的层级索引中使用的级别名称
  • verify_integrity : boolean, 默认 False. 检查新连接的轴是否包含了重复。这对性能的影响比较大,因为相关的底层数据连接原因。
  • copy : boolean, default True. 如果是False,没必要的时候就不会复制数据。

先不管其他的参数,在上面的例子中,当我们想要为上面的 df1,df2,df3 各绑定一个键的时候,我们可以这样做

result = pd.concat(frames, keys=['x', 'y', 'z'])
result.loc['y']

这似乎看不出来有什么用,是吧?后面会更加详细的解释。

需要注意的是 concat()(还有 append())会完整的复制数据,因此如果频繁使用的话,将会造成很大的性能问题。如果我们想要对几个数据集进行使用的话,最好还是使用列表的形式。

frames = [ process_your_file(f) for f in files ]
result = pd.concat(frames)

其他轴上设置逻辑

当归集多个数据集的时候,我们可以选择在其他轴上进行操作(而不是简单的进行连接)。这可以通过两种方式达成:

  1. 获取所有他们的联合,join=outter。默认操作,不会丢失信息
  2. 使用 join=inner

下面是对两个方法的例子,首先我们看使用 join=outter

df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
'D': ['D2', 'D3', 'D6', 'D7'],
'F': ['F2', 'F3', 'F6', 'F7']},
index=[2, 3, 6, 7])
result = pd.concat([df1, df4], axis=1, sort=False)

join=outer 的默认操作是排序其他轴(我们的例子中,就是列,因为我们沿行方向进行了连接)。在将来的版本中,可能会不进行排序,所以我们传递了 sort=False

使用 join=inner

result = pd.concat([df1, df4], axis=1, join='inner')

最后,假设我们想要使用原始数据中的 准确索引

result = pd.concat([df1, df4], axis=1).reindex(df1.index)

同样,我们可以在连接前进行索引设置:

pd.concat([df1, df4.reindex(df1.index)], axis=1)

append()

这个函数早于 concat(),演 axis=0 进行连接,命名索引:

result = df1.append(df2)

如果要连接的是 DataFrame,索引必须被拆开,而列就不用:

result = df1.append(df4, sort=False)

append() 也可以连接多个对象:

result = df1.append([df2, df3])

连接轴上忽略索引

在连接轴上没有有意义的索引时,我们可以进行忽略:

result = pd.concat([df1, df4], ignore_index=True, sort=False)

DataFrame.append() 也有一个参数:

result = df1.append(df4, ignore_index=True, sort=False)

不同维度数据连接

我们可以把 SeriesDataFrame 数据进行连接,Series 首先会进行转换成 DataFrame,而将列名,作为Series 的名称:

s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X')
result = pd.concat([df1, s1], axis=1)

如果 Series 没有命名(没列名),那么会使用数字进行命名:

s2 = pd.Series(['_0', '_1', '_2', '_3'])
result = pd.concat([df1, s2, s2, s2], axis=1)

传递 ignore_index=True 将会丢弃所有的名称引用,全采用数字命名:

result = pd.concat([df1, s1], axis=1, ignore_index=True)

分组键进行连接_keys

一个相当常用的的场景是,当我们用以存的 Series 来建立新的 DataFrame 时,用 keys 参数来重写列名。注意当列名存在的情况下的默认行为

s3 = pd.Series([0, 1, 2, 3], name='foo')
s4 = pd.Series([0, 1, 2, 3])
s5 = pd.Series([0, 1, 4, 5])
pd.concat([s3, s4, s5], axis=1)
pd.concat([s3, s4, s5], axis=1, keys=['red', 'blue', 'yellow'])

我们再看看最开始的例子:

result = pd.concat(frames, keys=['x', 'y', 'z'])

当我们传递字典的时候,则会用字典中的键作为 keys 参数:

pieces = {'x': df1, 'y': df2, 'z': df3}
result = pd.concat(pieces)
result = pd.concat(pieces, keys=['z', 'y'])

最后建立的多个索引会拥有级别,级别从传递的 keys 和原始的 DataFrame 中的索引来构建。

result.index.levels
FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])

如果我们想要指定其他的级别(其实很少用),我们可以传递 levels 参数:

result = pd.concat(pieces, keys=['x', 'y', 'z'],
levels=[['z', 'y', 'x', 'w']],
names=['group_key'])
result.index.levels
FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])

但这在使用类似 GroupBy 这样的操作时是有用的。

DataFrame 附加行

尽管不是非常有效(因为会创建新的对象),我们可以通过向 append() 传递一个 Series 或者 dict 来给 DataFrame 添加行:

s2 = pd.Series(['X0', 'X1', 'X2', 'X3'], index=['A', 'B', 'C', 'D'])
result = df1.append(s2, ignore_index=True)

这个时候 ignore_index=True 是必须的,如果我们想保留索引,那么我们的 DataFrame 索引必须是正确的建好了的。

我们也可以传递一个字典或者 Series 的列表:

dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
{'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
result = df1.append(dicts, ignore_index=True, sort=False)

数据库风格的 DataFrame或命名Series 的 Join/Merge

Pandas 提供了一个函数 merge(),来所有所有数据库的 join 操作的入口。

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=('_x', '_y'), copy=True, indicator=False,
validate=None)
  • left: DataFrame named Series object.

  • right: Another DataFrame or named Series object.

  • on: 进行 join 的 列或索引级别。在 left/right 参数上都必须找到. 如果不传递,同时 left_indexright_index 都是 False, 那么就会进行自动的推断。

  • left_on: 列或索引,作为 join 的 键. 可以是列名,索引名,或者是与数据有相同长度的数组。

  • right_on: 列或索引,作为 join 的 键. 可以是列名,索引名,或者是与数据有相同长度的数组。

  • left_index: 如果是 True, 使用左方数据的索引(行标签)作为 join 的键。如果数据有多个索引(有层级的),索引级别的数量必须和 右方数据的 join 键相等。

  • right_index: Same usage as left_index for the right DataFrame or Series

  • how: One of 'left', 'right', 'outer', 'inner'. 默认 inner. See below for more detailed description of each method.

  • sort: 通过 join 键排序. 默认 True, 设置成 False 很多时候会提高性能。

  • suffixes: Defaults to ('_x', '_y'),列名前缀

  • copy: 总是从传递的参数复制数据 (default True)

  • indicator: Add a column to the output DataFrame called _merge with information on the source of each row. _merge is Categorical-type and takes on a value of left_only for observations whose merge key only appears in 'left' DataFrame or Series, right_only for observations whose merge key only appears in 'right' DataFrame or Series, and both if the observation’s merge key is found in both.

  • validate : string, default None. If specified, checks if merge is of specified type.

    • “one_to_one” or “1:1”: checks if merge keys are unique in both left and right datasets.
    • “one_to_many” or “1:m”: checks if merge keys are unique in left dataset.
    • “many_to_one” or “m:1”: checks if merge keys are unique in right dataset.
    • “many_to_many” or “m:m”: allowed, but does not result in checks.

join() 方法,内部使用的其实是 merge() ,不过只在索引上进行。

简单说明

关系数据库中 join 操作的几个基本概念如下:

  • one-to-one joins: 例如在两个 DataFrame 的索引(必须包含不同值)上进行 joining
  • many-to-one joins: 例如在一个索引上 join 另外一个 DataFrame 的一个或多个列。
  • many-to-many joins: 多列 Join

对多对 join 的时候,索引将会被忽略。

left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})


right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})


result = pd.merge(left, right, on='key')
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})


right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})


result = pd.merge(left, right, on=['key1', 'key2'])
result = pd.merge(left, right, how='left', on=['key1', 'key2'])
result = pd.merge(left, right, how='right', on=['key1', 'key2'])
result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
result = pd.merge(left, right, how='inner', on=['key1', 'key2'])

重复键检查

merge 标识

合并 dtypes

索引 Join