【问题标题】:How to find intersection of two column rows of data frame that is grouped by and remove that value from cells that contains it?如何找到分组的数据框的两列行的交集并从包含它的单元格中删除该值?
【发布时间】:2019-10-29 15:17:35
【问题描述】:

我有一个如下的数据框:

name  teamA   teamB
foo    a        b
foo    b        c
foo    c        b
bar    a        e
bar    a        d
...

我想分别为每个名称查找行的交集,但对于列 teamA 和 teamB。然后删除包含该交集值的单元格的值。 在此示例中,对于名称“foo”,行的交集将是“b”,对于名称“bar”,将是“a”。 因此删除此交集值后的数据框将如下所示:

name  teamA   teamB
foo     a      " "
foo    " "      c
foo     c      " "
bar    " "      e
bar    " "      d
...

最近,我尝试将 teamA 和 teamB 作为以示例团队命名的列。

name   teams
foo    [a, b]
foo    [b, c]
foo    [c, b]
...

以后我想得到

name   teams
foo    [a, " "]
foo    [" ", c]
foo    [c, " "]
...

但我发现更建议将它分成两列,我发现答案很有趣,但我不知道如何将它应用于分组数据框。 https://stackoverflow.com/a/55554709/9168586(查看“在许多列上过滤”部分和“保留至少一列为真的行”)。 就像那个例子:

dataframe[['teamA', 'teamB']].isin('b').any(axis=1)

0     True
1     True
2     True
3     True
dtype: bool

其中“b”将是我将迭代的值(团队)之一。每次迭代后,如果整列为 True,我将从每行中的列 teamA 或 teamB 中删除该值并继续到另一个组。

我得到的错误是:

Cannot access callable attribute 'isin' of 'DataFrameGroupBy' objects, try using the 'apply' method

only list-like or dict-like objects are allowed to be passed to DataFrame.isin(), you passed a 'str'

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    我们可以做melt,然后删除重复的,然后pivot它回来

    s=df.reset_index().melt(['index','name']).\
          drop_duplicates(['name','value'],keep=False).\
             pivot_table(index=['index','name'],columns='variable',values='value',aggfunc='first').\
                fillna('').reset_index(level=1)
    s['team']=list(zip(s.teamA,s.teamB))
    s
    Out[102]: 
    variable name teamA teamB   team
    index                           
    0         foo     a        (a, )
    1         foo           c  (, c)
    2         foo           d  (, d)
    3         bar           e  (, e)
    4         bar           d  (, d)
    

    【讨论】:

    • 所以,我的问题显然不完整。如果我必须按两列分组怎么办?如果像下面的示例中有两个以上的重复项怎么办? name teamA teamB foo a b foo b c foo b c bar a e bar a d 我只想删除 b 值,因为它们显示在每一行中。我可以这样做,比如删除最常见的值或类似的东西,但我的主要问题是按两列分组。
    【解决方案2】:

    尝试groupby并申请stackdrop_duplicatesunstackfillna

    (df[['teamA', 'teamB']].groupby(df.name, sort=False)
                           .apply(lambda x: x.stack().drop_duplicates(keep=False))
                           .unstack().fillna('').reset_index('name'))
    
    Out[93]:
      name teamA teamB
    0  foo     a
    1  foo           c
    2  foo           d
    3  bar           e
    4  bar           d
    

    【讨论】:

    • 所以,我的问题显然不完整。如果我必须按两列分组怎么办?如果像下面的示例中有两个以上的重复项怎么办(我还将编辑我的主要问题,以便您可以看到它很好)? name teamA teamB foo a b foo b c foo b c bar a e bar a d 我只想删除 b 值,因为它们显示在每一行中。我可以这样做,比如删除最常见的值或类似的东西,但我的主要问题是按两列分组。
    【解决方案3】:

    也许不如@WeNYoBen sulution 好,但您可以考虑使用非常灵活的自定义函数

    import pandas as pd
    df = pd.DataFrame({"name":["foo"]*3+["bar"]*2,
                       "teamA":["a", "b", "b", "a", "a"],
                       "teamB":["b", "c", "d", "e", "d"]})
    
    
    def fun(x):
        toRemove = list(set(x["teamA"].values).intersection(x["teamB"]))
        for col in ["teamA", "teamB"]:
            x[col] = np.where(x[col].isin(toRemove), " ", x[col])
        return x
    
    
    df.groupby("name").apply(fun)
    

    哪个输出是:

      name teamA teamB
    0  foo     a      
    1  foo           c
    2  foo           d
    3  bar     a     e
    4  bar     a     d
    
    

    【讨论】:

      【解决方案4】:

      groupby.apply + Series.isin.

      示例数据帧:

      print(df)
      
        name teamA teamB
      0  foo     a     b
      1  foo     b     c
      2  foo     b     d
      3  bar     a     e
      4  bar     a     d
      5  bar     b     a
      

      new_df=df.copy()
      groups=df.groupby('name',sort=False)
      new_df['teamA']=groups.apply(lambda x: x['teamA'].mask(x['teamA'].isin(x['teamB']),' ')).reset_index(drop=True)
      new_df['teamB']=groups.apply(lambda x: x['teamB'].mask(x['teamB'].isin(x['teamA']),' ')).reset_index(drop=True)
      print(new_df)
      
        name teamA teamB
      0  foo     a      
      1  foo           c
      2  foo           d
      3  bar           e
      4  bar           d
      5  bar     b   
      

      然后使用DataFrame.apply + joinsplit 得到teams 列:

      new_df['teams']=new_df[['teamA','teamB']].apply(lambda x: ','.join(x).split(','),axis=1)
      print(new_df)
      
        name teamA teamB   teams
      0  foo     a        [a,  ]
      1  foo           c  [ , c]
      2  foo           d  [ , d]
      3  bar           e  [ , e]
      4  bar           d  [ , d]
      5  bar     b        [b,  ]
      

      【讨论】:

      • 您的回答很有用,但我的问题并不完整。看看我在主要问题中编辑的表格。
      【解决方案5】:

      在我昨天编辑了我的问题之后... 这是我的数据框(df):

      name  teamA   teamB year
      foo    a        b    1
      foo    b        c    1
      foo    c        b    1
      bar    a        e    2
      bar    a        d    2
      foo    a        h    2
      foo    h        c    2
      foo    h        b    2
      ...
      

      这是解决方案:

      def fun(x):
          melted = pd.melt(x.reset_index(), id_vars=['name', 'year'], value_vars=['teamA', 'teamB'], var_name='var_name',
                          value_name='team')
          toRemove = melted.team.mode().iloc[0]
          for col in ["teamA", "teamB"]:
              x[col] = x[col].replace(toRemove,'something')
          return x
      
      
      df = df.groupby(["name", "year"]).apply(fun)
      

      因此,我融化了我的数据框,并在从两列中删除该值之后找到最常见的值。 谢谢@rpanai!每个答案都有帮助,但你最!

      【讨论】:

        猜你喜欢
        • 2021-06-26
        • 2012-03-24
        • 2019-11-14
        • 1970-01-01
        • 1970-01-01
        • 2019-11-15
        • 1970-01-01
        • 1970-01-01
        • 2018-03-19
        相关资源
        最近更新 更多