如何按ID分组并用非null值标记第一行?

时间:2019-07-03 04:54:59

标签: python pandas

ID内,我需要删除第value > 0行,并删除数据帧中所有有序日期列的行。我认为最简单的方法是创建一个新的flag列以将这些行标记为要删除。

我想出了以下内容来标记每个ID中的第一个日期行(排序后),但是我很难弄清楚如何继续将我的标志移动到并包括{{ 1}}:

value > 0

哪个会吸引我:

df['flag'] = np.where((df.date == df.groupby('id')['date'].transform('flag')),1,0)

但最终结果应该是:

id  date        value   flag
114 2016-01-01  0       1
114 2016-02-01  0       0
114 2016-03-01  200     0
114 2016-04-01  300     0
114 2016-05-01  100     0
220 2016-01-01  0       1
220 2016-02-01  0       0
220 2016-03-01  0       0
220 2016-04-01  0       0
220 2016-05-01  400     0
220 2016-06-01  200     0

2 个答案:

答案 0 :(得分:2)

  1. 按升序排列的第一id和日期
  2. 使用ID
    的第一个非零值时填充标志1。
  3. 在标志
    中将nan替换为0
  4. 填充分组依据并进行转换
  5. 最终将Nan替换为0
df = pd.DataFrame(data={"id": [114, 114, 114, 114, 114, 220, 220, 220, 220, 220, 220],
                        "date": ['2016-01-01', '2016-02-01', '2016-03-01', '2016-04-01', '2016-05-01',
                                 '2016-01-01', '2016-02-01', '2016-03-01', '2016-04-01', '2016-05-01', '2016-06-01'],
                        'value': [0, 0, 200, 300, 100, 0, 0, 0, 0, 400, 200]})

df.sort_values(by=['id', 'date'], ascending=[True, True], inplace=True)
df['flag'] = 0
df.loc[df['value'].ne(0).groupby(df['id']).idxmax(),'flag']=1
df['flag'].replace({0:np.nan},inplace=True)

df['flag'] = df.groupby(['id'],as_index=False)['flag'].transform(pd.Series.bfill)
df['flag'].fillna(0,inplace=True)
print(df)
  id        date      value  flag
0   114  2016-01-01      0   1.0
1   114  2016-02-01      0   1.0
2   114  2016-03-01    200   1.0
3   114  2016-04-01    300   0.0
4   114  2016-05-01    100   0.0
5   220  2016-01-01      0   1.0
6   220  2016-02-01      0   1.0
7   220  2016-03-01      0   1.0
8   220  2016-04-01      0   1.0
9   220  2016-05-01    400   1.0
10  220  2016-06-01    200   0.0

我希望它能解决您的问题

答案 1 :(得分:2)

您无需创建中间flag变量就可以实现

假设您的数据如下所示:

     id       date  value
0   114 2016-01-01      0
1   114 2016-02-01    100
2   114 2016-03-01    200
3   114 2016-04-01    300
4   115 2016-01-01      0
5   115 2016-02-01      0
6   115 2016-03-01    100
7   115 2016-04-01    200
8   116 2016-01-01    100
9   116 2016-02-01      0    <-- notice the 0 value in the middle here
10  116 2016-03-01    330
11  116 2016-04-01    400

方法1

此方法假定不需要所有的0值,并且每个id组中的第一个实数值都将以0开头。

我们要做的只是删除所有零,然后对id进行分组并仅删除数据的第一行。这具有删除第一个实际行及其之前所有内容(假定为0)的作用

df1 = df[df['value'] > 0]
df1.sort_values('date').groupby('id', group_keys=False).apply(lambda g: g.iloc[1:])

    id       date  value
0  114 2016-03-01    200
1  114 2016-04-01    300
2  115 2016-04-01    200
3  116 2016-03-01    330
4  116 2016-04-01    400

方法2

如果每个id组的中间都为零(如上面数据中的第9行)怎么办?

从您的问题中不清楚在这种情况下您想做什么。按照您的描述,我假设您要进行的操作是找到第一个实际行(第8行),将其以及所有先前的内容(在这种情况下,没有先前的数据)删除,然后将0保留为空

此处的关键是使用first_valid_index()获取非NA / null的第一行数据,并将其用作.iloc中的索引

def remove_prev(g):
    out = g.replace({0: np.nan}).reset_index(drop=True)
    return out.iloc[out['value'].first_valid_index()+1:].fillna(0)

df.groupby('id', group_keys=False).apply(remove_prev).reset_index(drop=True)

      id       date  value
0  114.0 2016-03-01  200.0
1  114.0 2016-04-01  300.0
2  115.0 2016-04-01  200.0
3  116.0 2016-02-01    0.0
4  116.0 2016-03-01  330.0
5  116.0 2016-04-01  400.0

标记方法

如果您确实要设置标志变量,则可以再次使用first_valid_index()确定要设置flag=1的行:

def flag_prev(g):
    out = g.replace({0: np.nan})
    out.loc[:out['value'].first_valid_index(), 'flag'] = 1
    return out.fillna(0)

df.groupby('id', group_keys=False).apply(flag_prev).reset_index(drop=True)

       id       date  value  flag
0   114.0 2016-01-01    0.0   1.0
1   114.0 2016-02-01  100.0   1.0
2   114.0 2016-03-01  200.0   0.0
3   114.0 2016-04-01  300.0   0.0
4   115.0 2016-01-01    0.0   1.0
5   115.0 2016-02-01    0.0   1.0
6   115.0 2016-03-01  100.0   1.0
7   115.0 2016-04-01  200.0   0.0
8   116.0 2016-01-01  100.0   1.0
9   116.0 2016-02-01    0.0   0.0
10  116.0 2016-03-01  330.0   0.0
11  116.0 2016-04-01  400.0   0.0