用groupby进行大熊猫复杂处理

时间:2016-03-19 16:16:12

标签: python pandas

我的数据按ID分组。在每个组中,它按colB排序。我需要实现的逻辑如下:

如果colA为空,且colD为(2,3或4), 然后创建一个名为'flag'的列,并在colC的最后一个非零行中设置flag = 1。在该组的所有其他行中将该标志设置为0,其中colC为非零。 删除该特定分组的行(colA为空,colC为0)。

对所有其他'id'组重复上述步骤。

(对于colA非空白的行,我可以将标志设置为我需要的。)

以下是我的数据:

id  colA    ColB    colC      colD
1           10      1352.23   2
1           11      706.87    2
1           12      1116.6    2
1           13      0         2
1           14      0         2
1           15      0         2
2           2      6884.03    3
2           3      2235.97    3
2           4      3618.04    3
2           5      11745.42   3
3   2013    1      345.98     0

这是我在处理它之后想要获得的。

id  colA  ColB  colC      colD  flag
1         10    1352.23     2   0
1         11    706.87      2   0
1         12    1116.6      2   1
2          2    6884.03     3   0
2          3    2235.97     3   0
2          4    3618.04     3   0
2          5    11745.42    3   1
3   2013   1    345.98      0   0

该数据包含数千个此类分组。我希望有人可以帮我弄清楚上面处理的Python代码是什么样的。我对groupby函数有一个基本的了解,但是没有能够弄清楚如何做到这一点。

这是我尝试使用的代码。代码给出了错误: “AttributeError:'str'对象没有属性'id'。”

当我在colC中检测到我最终要删除的零时,我试图将“标志”设置为NaN,因此我可以在稍后的步骤中轻松删除它们。

def setFlag(grouped):
    for name, group in grouped:
        for i in range(group.id.size):
            drop_candidate = (
                     pd.isnull(group.iloc[i]['colA'])&
                  ( (group.iloc[i]['colD'] == 2) |
                    (group.iloc[i]['colD'] == 3) |
                    (group.iloc[i]['colD'] == 4)    ) 
                )

            last_nonZero = group[group != 0].index[-1]

            if (  (drop_candidate & (group.iloc[i]['colC'] == 0))  ):
                group['flag'] = np.nan
            elif ((drop_candidate & (group.iloc[i]['colC'] != 0)) & (last_nonZero != i)):
                group['flag'] = 0
            elif last_nonZero == i:
                group['flag'] = 1

        return grouped

df.groupby('id').apply(setFlag)

以下是重新创建测试数据帧的代码:

import pandas as pd
import numpy as np   
df = pd.DataFrame.from_items([
    ('id', [1,1,1,1,1,1,2,2,2,2,3]), 
    ('colA', [np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,2013]),
    ('colB', [10,11,12,13,14,15,2,3,4,5,1]),
    ('colC', [1352.23,706.87,1116.6,0,0,0,6884.03,2235.97,3618.04,11745.42,345.98]),
    ('colD', [2,2,2,2,2,2,3,3,3,3,0]),
    ('flag', [np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,]),
    ])

2 个答案:

答案 0 :(得分:0)

您的流程看起来有三个部分:

1)摆脱colA为null且colC == 0的行。先减少数据帧

如果是AND逻辑:

reduced_df = df.loc[(df.colA.notnull()) & (df.colC != 0), :].copy()

如果是OR逻辑:

reduced_df = df.loc[(df.colA.notnull()) | (df.colC != 0), :].copy()

    id  colA  colB      colC  colD  flag
0    1   NaN    10   1352.23     2   NaN
1    1   NaN    11    706.87     2   NaN
2    1   NaN    12   1116.60     2   NaN
6    2   NaN     2   6884.03     3   NaN
7    2   NaN     3   2235.97     3   NaN
8    2   NaN     4   3618.04     3   NaN
9    2   NaN     5  11745.42     3   NaN
10   3  2013     1    345.98     0   NaN

2)现在您已准备好处理标记组的最后一列的第二部分。由于默认标志值为0,因此以

开头

reduced_df.loc[:, 'flag'] = 0

3)您可以使用duplicated找到重复的值,然后确保colA为空

reduced_df.loc[~reduced_df.colD.duplicated(keep='last') & reduced_df.colA.isnull(), 'flag'] = 1

reduced_df

    id  colA  colB      colC  colD  flag
0    1   NaN    10   1352.23     2     0
1    1   NaN    11    706.87     2     0
2    1   NaN    12   1116.60     2     1
6    2   NaN     2   6884.03     3     0
7    2   NaN     3   2235.97     3     0
8    2   NaN     4   3618.04     3     0
9    2   NaN     5  11745.42     3     1
10   3  2013     1    345.98     0     0

答案 1 :(得分:0)

这是我使用apply方法提出的。我认为它符合您的要求:

df['flag'] = df['colD'].shift(-1) #use as a placeholder to compare consecutive 'colD' vals
df['flag'] = df.apply(lambda x: 1 if (x['flag']!=x['colD']) & 
                  (np.isnan(x['colA'])) & (x['colD']>0) else 0, axis=1) 

如果有效,请告诉我! (你需要将numpy作为np导入的btw)。此外,如果您想将此限制仅限于2,3&amp; 4,您必须将最后一部分从(x['colD']>0)更改为(x['colD']>1) & (x['colD'] < 5)