pandas explode 避免重复值

pandas explode avoid duplication of values

我在多列中有以下数据:

    col1             col2                       col3
123456     ['mary','ralph', ''bob']     ['bob','sam']
456789     ['george','fred', susie']  ['ralph','mary', 'bob']
789123     ['mary', bob']             ['bob']

我最终需要在每一列上添加一个 value_counts。为了将所有内容都从列表中删除,我正在尝试爆炸。我可以将值放入它们的列 post-explode,没问题。但是,那些了解 explode 的人都知道,我的 value_counts 会被膨胀,因为在将它应用到多个列时会重复 explode 导致的值

例如,分解产生这个:

  col1     col2     col3
123456     mary     bob
123456     mary     sam     
123456     mary     george
123456     ralph    bob
123456     ralph    sam     
123456     ralph    george...etc.

显然,这会导致每列的准确 value_counts 是我需要的。 我尝试在每一列上循环爆炸,然后在每一列爆炸后匹配第一列和爆炸列并删除重复项,但不起作用。总是喜欢不成为房间里最聪明的人(更多需要学习)所以我把这个问题发给了你 pandas 充满想法的大师。 (看看我在那里做了什么?)。谢谢

我可以 value_counts 除了 col1 之外的所有列的预期输出是这样的:

123456    mary     bob
123456   ralph     sam
123456     bob  george
456789  george   ralph
456789    fred    mary
456789   susie     bob
789123    mary     bob
789123     bob  george

我们可以使用 stack 展开每个列表,然后使用 cumcount

创建代理索引
# if not real lists you'll need `literal_eval
from ast import literal_eval

s = df.set_index('col1').stack().map(literal_eval).explode().to_frame()
df1 = s.set_index(s.groupby(level=[0,1]).cumcount(),append=True).unstack(1).droplevel(0,1)

print(df1)
            col2    col3
col1                    
123456 0    mary     bob
       1   ralph     sam
       2     bob  george
456789 0  george   ralph
       1    fred    mary
       2   susie     bob
789123 0    mary     bob
       1     bob  george

IIUC 你可以 apply 而不是循环和分解:

print (df.set_index("col1").apply(pd.Series.explode))

          col2    col3
col1                  
123456    mary     bob
123456   ralph     sam
123456     bob  george
456789  george   ralph
456789    fred    mary
456789   susie     bob
789123    mary     bob
789123     bob  george

对于不均匀列表:

s = df.set_index("col1").agg("sum").to_frame().explode(0)

print (s.groupby(level=0)[0].apply(pd.Series.value_counts))

col2  mary      2
      bob       2
      george    1
      susie     1
      ralph     1
      john      1
      fred      1
col3  bob       3
      george    2
      sam       1
      ralph     1
      mary      1
Name: 0, dtype: int64

或者:

s = df.set_index("col1").agg("sum").to_frame().explode(0)

print (s.reset_index().groupby(["index", 0]).size().unstack(0))

0      bob  fred  george  mary  ralph  sam  susie
index                                            
col2   2.0   1.0     1.0   2.0    1.0  NaN    1.0
col3   3.0   NaN     2.0   1.0    1.0  1.0    NaN

您可以在分解前合并列

df['col4']=df['col2']+df['col3']
df.drop(columns = ['col2','col3'],inplace = True)

然后在 'col4'

上爆炸

我想要列表中元素的value_counts,首先你需要展平列,然后以value_counts为例:

import pandas as pd
from itertools import chain

df = pd.DataFrame(data=[
    [123456, ['mary', 'ralph', 'bob'], ['bob', 'sam', 'george']],
    [456789, ['george', 'fred', 'susie'], ['ralph', 'mary', 'bob']],
    [789123, ['mary', 'bob'], ['bob', 'george']]
], columns=['col1', 'col2', 'col3'])

print(pd.Series(chain.from_iterable(df['col2'])).value_counts())

输出

mary      2
bob       2
susie     1
george    1
fred      1
ralph     1
dtype: int64

上面的结果是 value_counts for col2 你的例子。

您可以应用一个函数来获取每一列,flatten 它和 returns 该列的 value_counts。然后用 0 替换 NaN 值并将返回的帧转换为整数以整理输出:

import pandas as pd
from pandas.core.common import flatten

def nested_valuecounts(series):
    flattened = list(flatten(series))
    return pd.Series.value_counts(flattened)

out = df[["col2", "col3"]].apply(nested_valuecounts).fillna(0).astype(int)

print(out)
        col2  col3
bob        1     3
fred       1     0
george     1     2
mary       2     1
ralph      1     1
sam        0     1
susie      1     0

你可以试试:

df.melt('col1').explode('value')\ #melt col2 and col3 into one column and explode
  .groupby(['variable','value'])\ #Groupby melted columns
  .count()['col1']\ #count
  .unstack(0, fill_value=0)  #reshape to show counts per col2 and col3 by name

输出:

variable  col2  col3
value               
bob          2     3
fred         1     0
george       1     0
mary         2     1
ralph        1     1
sam          0     1
susie        1     0