【问题标题】:Pandas transform inconsistent behavior for list熊猫转换列表的不一致行为
【发布时间】:2020-01-04 17:09:38
【问题描述】:

我有按预期工作的示例 sn-p:

import pandas as pd

df = pd.DataFrame(data={'label': ['a', 'b', 'b', 'c'], 'wave': [1, 2, 3, 4], 'y': [0,0,0,0]})
df['new'] = df.groupby(['label'])[['wave']].transform(tuple)

结果是:

  label  wave  y     new
0     a     1  0    (1,)
1     b     2  0  (2, 3)
2     b     3  0  (2, 3)
3     c     4  0    (4,)

如果我在转换中而不是 tuple 而不是 set, frozenset, dict,它的工作原理类似,但如果我给出 list,我会得到完全出乎意料的结果:

df['new'] = df.groupby(['label'])[['wave']].transform(list)

  label  wave  y  new
0     a     1  0    1
1     b     2  0    2
2     b     3  0    3
3     c     4  0    4

有一种解决方法可以获得预期的结果:

df['new'] = df.groupby(['label'])[['wave']].transform(tuple)['wave'].apply(list)

  label  wave  y     new
0     a     1  0     [1]
1     b     2  0  [2, 3]
2     b     3  0  [2, 3]
3     c     4  0     [4]

我考虑了可变性/不变性(列表/元组),但对于 set/frozenset,它是一致的。

问题是为什么它会以这种方式工作?

【问题讨论】:

    标签: python pandas transform pandas-groupby


    【解决方案1】:

    建议的答案不再适用于 Pandas 1.2.4。这是一个解决方法:

    df.groupby(['label'])[['wave']].transform(lambda x: [list(x) + [1]]*len(x))
    

    其背后的想法与其他答案中解释的相同(例如@Allen 的答案)。因此,这里的解决方案是将函数包装到另一个列表中,并重复它与组长度相同的数字,这样当pandas变换展开它时,每一行都会得到内部列表。

    输出:

        wave
    0   [1, 1]
    1   [2, 3, 1]
    2   [2, 3, 1]
    3   [4, 1]
    

    【讨论】:

      【解决方案2】:

      由于 DataFrames 主要设计用于处理 2D 数据,包括数组而不是标量值可能会偶然发现诸如此类的警告。

      pd.DataFrame.trasnform最初是在.agg之上实现的:

      # pandas/core/generic.py
      @Appender(_shared_docs["transform"] % dict(axis="", **_shared_doc_kwargs))
      def transform(self, func, *args, **kwargs):
          result = self.agg(func, *args, **kwargs)
          if is_scalar(result) or len(result) != len(self):
              raise ValueError("transforms cannot produce " "aggregated results")
      
          return result
      

      但是,transform 总是返回一个必须与 self 具有相同长度的 DataFrame,这本质上是输入。

      当您在DataFrame 上执行.agg 函数时,它可以正常工作:

      df.groupby('label')['wave'].agg(list)
      label
      a       [1]
      b    [2, 3]
      c       [4]
      Name: wave, dtype: object
      

      transform 尝试返回具有相同长度的Series 时,问题就出现了。

      在转换groupby 元素的过程中,该元素是self 的一个切片,然后再次连接它,列表被解包到与@Allen 提到的相同长度的索引。

      但是,当它们不对齐时,请不要解包:

      df.groupby(['label'])[['wave']].transform(lambda x: list(x) + [1])
          wave
      0   [1, 1]
      1   [2, 3, 1]
      2   [2, 3, 1]
      3   [4, 1]
      

      解决此问题的方法可能是避免transform

      df = pd.DataFrame(data={'label': ['a', 'b', 'b', 'c'], 'wave': [1, 2, 3, 4], 'y': [0,0,0,0]})
      df = df.merge(df.groupby('label')['wave'].agg(list).rename('new'), on='label')
      df
          label   wave    y   new
      0   a         1     0   [1]
      1   b         2     0   [2, 3]
      2   b         3     0   [2, 3]
      3   c         4     0   [4]
      

      【讨论】:

      • 是的,我知道这个解决方法,直到这个问题我认为:transform = groupby.应用 + 合并,只是一个合体糖
      【解决方案3】:

      我认为这是熊猫的一个错误。请问可以在their github页面开票吗?

      起初我想,可能是因为list 没有正确处理为.transform 的参数,但如果我这样做了:

      def create_list(obj):
          print(type(obj))
          return obj.to_list()
      
      df.groupby(['label'])[['wave']].transform(create_list)
      

      我得到了同样的意外结果。但是,如果使用agg 方法,则可以直接使用:

      df.groupby(['label'])['wave'].agg(list)
      Out[179]: 
      label
      a       [1]
      b    [2, 3]
      c       [4]
      Name: wave, dtype: object
      

      我无法想象这是预期的行为。

      顺便说一句。我还发现不同的行为可疑,如果您将元组应用于分组系列和分组数据帧,就会出现这种情况。例如。如果将transform 应用于系列而不是DataFrame,则结果也不是包含列表的系列,而是包含ints 的系列(请记住[['wave']],它创建了一个单列数据框transform(tuple) 确实返回了元组):

      df.groupby(['label'])['wave'].transform(tuple)
      Out[177]: 
      0    1
      1    2
      2    3
      3    4
      Name: wave, dtype: int64
      

      如果我用 agg 而不是 transform 再次这样做,它适用于 ['wave'][['wave']]

      我在 ubuntu X86_64 系统上使用 0.25.0 版本进行测试。

      【讨论】:

      • 我已经创建了一个问题 github.com/pandas-dev/pandas/issues/28246 关于 ['wave'] 与 [['wave']] 的事情是你的发现,它是单独的问题,所以如果你创建会更好仅在 github 上发布此问题。非常感谢!
      • 你不能把它加到你的票上吗?我必须经历整个过程才能为此设置一个测试用例。
      【解决方案4】:

      我以前遇到过类似的问题。我认为潜在的问题是当列表中的元素数量与组中的记录数匹配时,它会尝试解包列表,以便列表的每个元素都映射到组中的记录。

      例如,这将导致列表解包,因为列表的 len 与每个组的长度匹配:

      df.groupby(['label'])[['wave']].transform(lambda x: list(x))
          wave
      0   1
      1   2
      2   3
      3   4
      

      但是,如果列表的长度与每个组不同,您将获得所需的行为:

      df.groupby(['label'])[['wave']].transform(lambda x: list(x)+[0])
      
          wave
      0   [1, 0]
      1   [2, 3, 0]
      2   [2, 3, 0]
      3   [4, 0]
      

      我认为这是列表解包功能的副作用。

      【讨论】:

      • 很好的观察!看起来这是他们如何在内部表示中间结果的问题。但我认为这仍然应该被视为一个错误。
      • 对我来说,这种行为令人困惑。如果对此没有简单的补救措施,则应放弃对转换列表的支持,或者至少应添加一些警告。
      • @QuantChristo,我同意这是一个非常令人困惑的行为,我之前遇到的类似问题花了我一段时间才弄清楚原因。也许您可以将其作为错误提交。
      • @Shinjo 是pandas相关的,type(df[['wave']])是DataFrame,type(df['wave'])是Series。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 2021-10-20
      • 1970-01-01
      • 2019-10-12
      • 1970-01-01
      相关资源
      最近更新 更多