【问题标题】:How can a pandas merge preserve order?熊猫合并如何保持顺序?
【发布时间】:2013-11-26 00:59:09
【问题描述】:

我在 pandas 中有两个 DataFrame,试图合并它们。但是熊猫不断改变顺序。我已经尝试设置索引,重置它们,无论我做什么,我都无法让返回的输出具有相同顺序的行。有诀窍吗? 请注意,我们从贷款顺序“a,b,c”开始,但在合并之后,它是“a,c,b”。

import pandas
loans = [  'a',  'b', 'c' ]
states = [  'OR',  'CA', 'OR' ]
x = pandas.DataFrame({ 'loan' : loans, 'state' : states })
y = pandas.DataFrame({ 'state' : [ 'CA', 'OR' ], 'value' : [ 1, 2]})
z = x.merge(y, how='left', on='state')

但现在顺序不再是原来的'a,b,c'。有任何想法吗?我使用的是熊猫版本 11。

【问题讨论】:

  • 一方面,您需要传递sort=False 否则它将根据您不想要的连接键进行排序。但这还不足以解决问题;未排序的顺序仍会将来自同一源行的所有行组合在一起。一个简单的解决方法是执行x.merge(x.merge(y, how='left', on='state', sort=False)),它将x 中的每一行与合并中的相应for 合并,从而恢复x 的原始顺序。但希望现在有一个更好的解决方案正在逃离我的大脑。
  • @abarnert,我认为我们可以使用.join().update()(出于某种原因)它们确实保留了订单。

标签: python pandas


【解决方案1】:

希望有人能提供更好的答案,但万一没有人这样做,这肯定会奏效,所以……

Zeroth,我假设您不希望只是在 loan 上排序,而是要保留 whatever 原始顺序在 x 中,可能有也可能没有任何东西与loan 列的顺序有关。 (否则,问题就更简单了,也没有那么有趣了。)

首先,您要求它根据连接键进行排序。正如the docs 解释的那样,当您不传递sort 参数时,这是默认设置。


其次,如果您根据连接键进行排序,这些行最终会组合在一起,这样从同一源行合并的两行最终会彼此相邻,这意味着你仍然会得到acb

您可以通过将行按照它们在原始x 中出现的顺序组合在一起来解决此问题,只需再次与x 合并(在任一侧,这并不重要),或者通过基于重新索引如果您愿意,请在x 上。像这样:

x.merge(x.merge(y, how='left', on='state', sort=False))

或者,您可以使用reset_index 在其中填入一个 x-index,然后对其进行排序,如下所示:

x.reset_index().merge(y, how='left', on='state', sort=False).sort('index')

显然,无论哪种方式都显得有点浪费和笨拙......所以,正如我所说,希望有一个更好的答案,我只是暂时没有看到。但如果没有,那就行了。

【讨论】:

  • 是的,这似乎可行。看起来很浪费,但确实有效。谢谢。
  • @Wes Mckinney:我无法解释为什么索引会重新排序。这很令人沮丧,因为我整个下午都在处理这个问题,认为这是我的代码中的一个错误。任何解释将不胜感激!
  • @abarnert 谢谢。这也适用于非left 合并吗?它产生工作代码,但很难看出结果是否正确(我猜它有效,因为使用和不使用这种排序的 nb 行之后是相同的......)
  • 对于pd.merge(x,y),默认为sort=True,但对于x.merge(y),默认为sort=False
  • 我在最新版本的 Pandas 中需要 .sort_index() 而不是 .sort('index')
【解决方案2】:

我可能有一个更简单的解决方案:

df_z = df_x.join(df_y.set_index('state'), on = 'state')

希望对你有帮助

【讨论】:

  • 如果statey DataFrame 中是某种索引(或相当于数据库表的主键),并且您尝试使用此索引将value 添加到每个@ 987654325@ 的状态来自y,那么这个解决方案无疑是最合适的。
  • 是的,在我的示例中,对于 df_y,State 是唯一的并且存在 (KEY)。顺序将与 df_x 相同,不考虑 df_y 的顺序
【解决方案3】:

我发现合并和恢复顺序的最快方法 - 如果您要合并“左” - 是在合并之前将原始顺序作为列包含在左侧数据框中,然后在合并后使用它来恢复顺序:

import pandas
loans = [  'a',  'b', 'c' ]
states = [  'OR',  'CA', 'OR' ]
x = pandas.DataFrame({ 'loan' : loans, 'state' : states })
y = pandas.DataFrame({ 'state' : [ 'CA', 'OR' ], 'value' : [ 1, 2]})

import numpy as np
x["Order"] = np.arange(len(x))

z = x.merge(y, how='left', on='state').set_index("Order").ix[np.arange(len(x)), :]

这种方法比排序更快。这是一个函数:

def mergeLeftInOrder(x, y, on=None):
    x = x.copy()
    x["Order"] = np.arange(len(x))
    z = x.merge(y, how='left', on=on).set_index("Order").ix[np.arange(len(x)), :]
    return z

【讨论】:

  • 甚至(稍微)更快、更易读的版本:``` def mergeLeftInOrder2(x, y, on=None): x = x.copy() x["Order"] = np.arange (len(x)) z = x.merge(y, how='left', on=on).sort("Order") return z.drop("Order", 1) ```
  • 我实际上什至无法再重现 OP 的问题 - 在 pandas 0.15.2 上似乎不是问题,所以所有时间问题似乎都没有实际意义。我想我也必须将其设为 .loc 而不是 .ix - 监督。当我最初提出这个解决方案时(很久以前)我有一个不同版本的熊猫,所以我想我会保持原样。
  • 我能够通过稍微更改输入来重新创建问题。见下文。这里的函数(mergeLeftInOrder)修复了它。进口熊猫贷款= ['a','b','c','d']状态= ['CA','IL','CA','OR'] x = pandas.DataFrame({'loan' : 贷款, 'state' : states }) y = pandas.DataFrame({ 'state' : [ 'OR', 'CA' ], 'value' : [ 1, 2]}) z = x.merge(y, how='left', on='state')
【解决方案4】:

Pandas 有一个merge_ordered 函数,因此您的解决方案现在很简单:

z = pd.merge_ordered(x, y, on='state')

【讨论】:

  • 这是用于时间序列的。
【解决方案5】:

使用pd.merge_ordered(),文档here

以你的为例,

z = pd.merge_ordered(x, y, how='left', on='state')

编辑:只是想指出这个函数的默认行为是外部合并,不同于更常见的.merge()的默认行为

【讨论】:

  • 不保留原始数据帧的顺序,而只是对输出数据帧进行排序。
猜你喜欢
  • 2023-01-04
  • 1970-01-01
  • 2018-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 1970-01-01
相关资源
最近更新 更多