【问题标题】:In Pandas, creating a new data frame using data filtered by a list of lists在 Pandas 中,使用由列表列表过滤的数据创建一个新的数据框
【发布时间】:2019-02-14 01:11:44
【问题描述】:

所以,我环顾四周,并没有找到这个问题的答案。如果它确实存在,我深表歉意。

我有一个看起来像这样的 DF:

a = pd.DataFrame({'Name': ['apple', 'banana', 'orange', 'apple', 'banana','orange'], 
                  'Units': [2,4,6,5,4,3]})

我也有一个这样的列表:

b = [['apple', 'banana'],['orange']]

我们的目标是将苹果和香蕉归为 1 列,将橙色归为另一列,并将它们各自的单位相加。列中的变量将显示为子列表中的第一项。 (没有子列表会有重复)。

这是我希望输出 df 的样子:

output = pd.DataFrame({'Name': ['apple', 'orange'],
               'Units': [15, 9]})

这是我现在的位置:

for fruit in a['Name']:
for sublist in b:
    if fruit in sublist:
        pd.concat([XYZ,
                   pd.DataFrame({'Name': sublist[0], 'Units': a[a.Name == fruit]['Units'].sum(), index=[0})], 
                  axis=1)

XYZ 是一个空数据框,其中包含我试图用结果填充的列 = 名称和单位。当水果在子列表中及其单位的总和时,我真的不明白如何创建数据框。

有什么想法吗? :D

编辑:子列表可以是 1 到 300 个项目。这里的代码只是一个更大的数据争论问题的 MWE。很抱歉没有提到这一点。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    确实,您可以在一行中做到这一点:

    sum_a = a.replace({"banana": "apple"}).groupby("Name", as_index=False).sum()
    

    【讨论】:

    • 这是一个非常聪明的解决方案,我没有想到。但是,它在这种情况下不起作用,因为列表列表实际上很大,a 中的 Name 列也是如此。这就是为什么我要遍历子列表以检查 a.Name 是否在子列表中。我可能应该提到这一点,那是我的错。
    【解决方案2】:

    IIUC,最好重新创建您的对象,而不是更改原始 df ,因为 replace 仍然丢失有关 apple 的信息,因为您将 apple 替换为香蕉。所以输出将只包含信息关于applebanana

    d={','.join(x):a.loc[a.Name.isin(x),'Units'].sum() for x in b }
    pd.Series(d)
    apple,banana    15 # here you do not losing the information of each items in the list 
    orange           9
    dtype: int64
    

    【讨论】:

    • 我真的很喜欢你的解决方案,我会把它保存在某个地方。然而,在这个用例中,丢失苹果或香蕉的信息是设计使然,它有不同的用途
    【解决方案3】:

    使用pd.Series.isin 和布尔索引:

    pd.DataFrame([(l[0], a.Units[a.Name.isin(l)].sum()) for l in b], columns=['Name', 'Units'])
         Name  Units
    0   apple     15
    1  orange      9
    

    【讨论】:

    • 我不在家测试这个,但我觉得这可能是我正在寻找的。我用 isin() 搞砸了一段时间,但无法让它正常工作。测试后,我将选择它作为所需的解决方案。我感谢您的帮助! :)
    • @AbdullahShah 很高兴为您提供帮助 :)
    【解决方案4】:

    另一种解决方案是创建一个返回名称和总和值的函数。

    from operator import itemgetter
    
    first = itemgetter(0)
    
    def make_rows(cols, df):
        for col in cols:
            name = first(col)
            val = df.loc[df.Name.str.contains('|'.join(col), regex=True), 'Units'].sum()
            yield name, val
    
    df1 = pd.DataFrame(make_rows(b, a), columns=a.columns)
    
    print(df1)
    
         Name  Units
    0   apple     15
    1  orange      9
    

    另外还有这样的:

    from functools import partial
    
    def make_rows(df, col):
        name = first(col)
        val = df.loc[df.Name.str.contains('|'.join(col), regex=True), 'Units'].sum()
        return name, val
    
    p = partial(make_rows, a)
    
    pd.DataFrame(list(map(p, b)), columns=a.columns)
    
         Name  Units
    0   apple     15
    1  orange      9
    

    【讨论】:

    • 这看起来像漂亮的代码,我一定会玩弄它。与按比例放大时克里斯的答案相比,添加一个函数会降低性能吗? (只是好奇)
    • 我认为这可以很好地扩展,因为它会产生值并且不会在内存中保存太多,你为什么不自己在更大的数据上测试它并看看
    猜你喜欢
    • 1970-01-01
    • 2014-07-02
    • 1970-01-01
    • 2017-04-30
    • 2023-01-17
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多