首页 > 解决方案 > python3 pandas:如何快速找到具有值的最新月份数不为0

问题描述

有如下数据框:

id  year month y sinx
 1  2019  1    0   1
 1  2019  2    0   2
 1  2019  3    1   3
 1  2019  4    0   4 
 1  2019  5    0   5
 1  2019  6    0   6
 1  2019  7    0   7
 1  2019  8    2   8
 1  2019  9    0   9
 1  2019  10   0   10
 1  2019  11   0   11
 1  2019  12   0   11
 1  2020  1    0   12
 1  2020  2    0   13
 1  2020  3    2   14
 1  2020  4    0   15 
 2  2019  1    0   1
 2  2019  2    0   2
 2  2019  3    0   3
 2  2019  4    0   4 
 .......

我想获取每个月之前每个 id 值(y 列)不为 0 的月数,如果没有上个月或没有上个月的值不为 0,只需将值设置为-1。

例如作为上面的数据框,我想得到以下结果。此外,Dataframe 大约 5M 很大。速度应该很快:

 id  year month y sinx  num_month
 1  2019  1    0   1      -1
 1  2019  2    0   2      -1
 1  2019  3    1   3      -1
 1  2019  4    0   4       1
 1  2019  5    0   5       2
 1  2019  6    0   6       3
 1  2019  7    0   7       4
 1  2019  8    2   8       5
 1  2019  9    0   9       1
 1  2019  10   0   10      2
 1  2019  11   0   11      3
 1  2019  12   0   11      4
 1  2020  1    0   12      5
 1  2020  2    0   13      6
 1  2020  3    2   14      7
 1  2020  4    0   15      1 
 2  2019  1    0   1       -1
 2  2019  2    0   2       -1
 2  2019  3    1   3       -1
 2  2019  4    0   4       1 
 .......

标签: pandaspython-3.6

解决方案


获取累积计数没问题,但获取-1值的逻辑有点棘手。这些都是矢量化的 pandas 方法,因此它应该在数百万行上都可以执行:

  1. 您可以获取必要的groupby列以及cumsumycumcount()
  2. 但是,您想再做cumcount一行,所以我修复了每组的最后一行np.where()
  3. 然后稍微棘手的部分是将值更改为-1. 我使用与前面步骤类似的技术来实现它,最终根据一些条件mask将相关值更改为。-1

m = df.groupby(['id', 'y', df['y'].cumsum()]).cumcount() + 1                 ########## setup you base getting cumcount
df['num_month'] = np.where((m == 1) & (m.shift() > 1), m.shift() + 1, m).astype(int)  # extend cumcount one further row per group for first line of code
s1 = df.groupby('id').transform('idxmin').iloc[:,0]                      ############## get index location of first value per group and return as series with same length
s2 = df.groupby(['id', (df['y'] > 0).cumsum()]).transform('idxmin').iloc[:,0] ######### get index location of first non-zero value per group and return as series with same length
df['num_month'] = df['num_month'].mask((s1 == s2) | (s1 == s2.shift()), -1) ########### using s1 and s2 conditions, update the necessary rows to -1
df
Out[1]: 
    id  year  month  y  sinx  num_month
0    1  2019      1  0     1         -1
1    1  2019      2  0     2         -1
2    1  2019      3  1     3         -1
3    1  2019      4  0     4          1
4    1  2019      5  0     5          2
5    1  2019      6  0     6          3
6    1  2019      7  0     7          4
7    1  2019      8  2     8          5
8    1  2019      9  0     9          1
9    1  2019     10  0    10          2
10   1  2019     11  0    11          3
11   1  2019     12  0    11          4
12   1  2020      1  0    12          5
13   1  2020      2  0    13          6
14   1  2020      3  2    14          7
15   1  2020      4  0    15          1
16   2  2019      1  0     1         -1
17   2  2019      2  0     2         -1
18   2  2019      3  1     3         -1
19   2  2019      4  0     4          1

推荐阅读