【问题标题】:Simplify code. Mine works but looks ugly like hell简化代码。我的工作,但看起来像地狱一样丑陋
【发布时间】:2020-06-11 21:25:33
【问题描述】:

我在处理 Python 时遇到了麻烦。我有 PD 数据框,我需要删除所有行,这些行是在特定值之后出现的(在代码中,值被写入称为年份的列表)所有值都应该按公司名称分组,因为这些观察属于列'inn_main 中提到的公司'。我真的不明白如何使用 Pandas 功能做到这一点。虽然我找到了一种方法,但它非常丑陋且效率不高。

import pandas as pd 
data = {'inn_main':['Apple', 'Apple', 'Apple', 'Apple', 'Tesla', 'Tesla', 'Tesla'], 'Help':['OK', 'OK', 2013, 'OK','OK', 2014, 'OK']} 
df = pd.DataFrame(data) 
droper=[]
block=0
years=[2012,2013,2014,2015,2016,2017,2018]
for j in range(len(list(df['Help'].groupby(df['inn_main'])))):
    alarm=0
    for i in range(len(list(df['Help'].groupby(df['inn_main']))[j][1].index.values)):
        if list(df['Help'].groupby(df['inn_main']))[j][1][list(df['Help'].groupby(df['inn_main']))[j][1].index.values[i].astype(int)] in years:
            block=1
            alarm=1
        if block==1:
            block=0
        else:
            if alarm==1:
                droper.append(list(df['Help'].groupby(df['inn_main']))[j][1].index.values[i].astype(int))   
df.drop(index=droper)

你能告诉我我到底做错了什么吗?有什么捷径吗?

【问题讨论】:

  • 发布一个您希望 DataFrame 输出看起来像什么的示例
  • 你能试着用文字解释你正在使用的逻辑吗?

标签: python pandas pandas-groupby


【解决方案1】:

解决方案非常短:

df.groupby('inn_main').apply(lambda grp:
    grp[~grp.Help.isin(years).cumsum().shift(fill_value=0).astype(bool)])

结果是:

           inn_main  Help
inn_main                 
Apple    0    Apple    OK
         1    Apple    OK
         2    Apple  2013
Tesla    4    Tesla    OK
         5    Tesla  2014

详情:

  • df.groupby('inn_main') - 按公司对 DataFrame 进行分组。
  • apply(lambda grp: - 对每个组应用一个 lambda 函数。
  • grp.Help.isin(years) - 帮助值是吗?
  • .cumsum() - 上述问题的累积总和。
  • shift(fill_value=0) - 将结果向下移动 1 行,填充 任何带有 0NaN(实际上是第一项)。
  • astype(bool) - 将整数转换为 bool
  • ~ - 否定上述结果
  • [...] - 在布尔索引中使用上述结果。
  • grp[...] - 从当前组返回“好”行(它们将 成为当前组结果的一部分)。

注意ASGM 的解决方案仅删除第一行 OKyears 中包含 Help 的任何行之后。

为了确认,添加另一行包含 TeslaOK 到 你的 DataFrame 并运行他的代码。

结果是:

  inn_main  Help
0    Apple    OK
1    Apple    OK
2    Apple  2013
4    Tesla    OK
5    Tesla  2014
7    Tesla    OK

所以存在索引 == 7(应该删除)的行。

【讨论】:

  • 我不知道这是否是 OP 想要的 - 这可能是许多情况下的正确解决方案!
  • 我不同意。 OP 写得很清楚:drop all 行,这些行是在特定值之后(不仅仅是第一个)。
  • 非常感谢,我真的打算在特定值之后删除所有行。
【解决方案2】:

我不完全理解您的要求,但这就是我认为您想要的。给定以下数据框...

   Help inn_main
0    OK    Apple
1    OK    Apple
2  2013    Apple
3    OK    Apple
4    OK    Tesla
5  2014    Tesla
6    OK    Tesla

...您想要识别df.Helpyears 列表中的每一行,并删除它之后的行(在属于给定公司的行中)。如果这是正确的,您可以更简单地做到这一点:

years = range(2012, 2019)
df[~df.groupby('inn_main')['Help'].apply(lambda g: g.isin(years).shift().fillna(False))]

这将给出:

   Help inn_main
0    OK    Apple
1    OK    Apple
2  2013    Apple
4    OK    Tesla
5  2014    Tesla

如果您想删除所有公司内跟随给定行的行,完整的answer by Valdi_Bo 说明了如何。

【讨论】:

    【解决方案3】:

    我不太了解 panda 也不知道你想做什么,但这里是重构为迭代列表而不是使用索引的代码:

    import pandas as pd 
    data = {'inn_main':['Apple', 'Apple', 'Apple', 'Apple', 'Tesla', 'Tesla', 'Tesla'], 'Help':['OK', 'OK', 2013, 'OK','OK', 2014, 'OK']} 
    df = pd.DataFrame(data) 
    droper=[]
    years=[2012,2013,2014,2015,2016,2017,2018]
    for j in list(df['Help'].groupby(df['inn_main'])):
        alarm=False
        for i in j[1].index.values:
            if j[1][i.astype(int)] in years:
                alarm=True
            elif alarm:
                droper.append(i.astype(int))   
    df.drop(index=droper)
    

    【讨论】:

    • 哈哈,上面的那个人对熊猫很了解,他用 1 个字符串做到了:D
    猜你喜欢
    • 2022-11-26
    • 1970-01-01
    • 2011-02-03
    • 1970-01-01
    • 2017-04-23
    • 1970-01-01
    • 2019-12-04
    • 2020-09-30
    • 1970-01-01
    相关资源
    最近更新 更多