2017-04-19 60 views
2

我需要找到'participant_id',其中包含具有特定长度的相同元素(NaN)的块。例如,请考虑以下df在熊猫中查找相同连续元素的块(及其大小)

 summary participant_id 
13865  3.0    28 
13995  NaN    28 
14050  3.0    28 
14219  5.0    28 
14346  NaN    28 
14364  4.0    28 
14456  4.0    28 
14680  NaN    28 
14733  3.0    28 
14913  2.0    28 
15007  4.0    28 
15107  4.0    28 
15280  NaN    28 
15287  3.0    28 
15420  2.0    28 
15521  2.0    28 
15756  NaN    28 
15758  3.0    28 
15973  NaN    28 
16038  4.0    28 
16079  6.0    28 
16215  4.0    28 
16412  NaN    28 
16506  6.0    28 
16543  6.0    28 
16649  2.0    28 
16811  NaN    28 
16911  NaN    28 
16928  3.0    28 
17028  2.0    28 
11582  NaN    27 
11718  2.0    27 
11843  NaN    27 
11941  2.0    27 
12053  NaN    27 
12142  NaN    27 
12269  NaN    27 
12367  4.0    27 
12510  NaN    27 
12632  NaN    27 
12732  NaN    27 
12796  2.0    27 
12946  NaN    27 
13059  NaN    27 
13126  2.0    27 
13312  NaN    27 
13394  3.0    27 
13427  2.0    27 
13618  NaN    27 
13707  NaN    27 
13832  NaN    27 
13945  NaN    27 
14087  NaN    27 
14199  NaN    27 
14299  NaN    27 
14398  NaN    27 
14520  NaN    27 
14639  NaN    27 
14759  NaN    27 
14897  NaN    27 
15013  NaN    27 
15116  NaN    27 
15182  3.0    27 
15319  NaN    27 
15437  NaN    27 
15518  3.0    27 
15695  NaN    27 
15812  NaN    27 
15821  2.0    27 
15933  2.0    27 

,如果im兴趣的连续4周以上的NaN块,比唯一的选择将是participant_id = 27,如果我想blocks_length = 2,比答案是participant_id = [27,28]

我试图按照类似solution,但它没有奏效。

回答

1

你可以指望用定制功能和groupby consecutives NaN

N = 4 
def f(x): 
    a = x.isnull() 
    return a.cumsum()-a.cumsum().where(~a).ffill().fillna(0) == N 
mask = df.groupby('participant_id', sort=False)['summary'].apply(f) 
L = df.loc[mask, 'participant_id'].unique().tolist() 
print (L) 

替代解决方案:

from functools import reduce 

N = 4 
nulls = df['summary'].isnull() 
df1 = nulls.groupby(df['participant_id']).expanding() \ 
      .apply(lambda i: reduce(lambda x, y: x+1 if y==1 else 0, i, 0)) 

L = df1[df1 == N].index.get_level_values(0).unique().tolist() 
print (L) 
1

groupby有助于单独获得每个参与者的数据。然后你可以用任何你想要的方式计算出数字。明确和简单的一个,而无需使用熊猫的力量可能是这样

block_size = 4 
for name, gr_data in data.groupby("participant_id"): 
    counter = 0 
    for value in gr_data["summary"]: 
     if value is None: 
      counter+=1 
      if counter>=block_size: 
       print("%s has block of NaN of length >= %d"%(str(name), block_size)) 
       break 
     else: 
      counter = 0 
1
def null_blocks(x, n): 
    isnull = np.isnan(x.values) 
    nextnot = np.append(~isnull[1:], True) 
    csum = isnull.cumsum() 
    return np.diff(csum[isnull & nextnot]).max() >= n 

def which_ids(k): 
    return [n for n, g in df.groupby('participant_id').summary if null_blocks(g, k)] 

演示

which_ids(2) 

[27, 28] 

which_ids(4) 

[27] 

它是如何工作

  • null_blocks
    • 我开始用布尔一系列其中的值NaNnp.isnan
    • 由于bool是子类的int我们可以总结起来有cumsum
    • 然后,我们可以找出其中框以isnull的否定结束并将其移位一个空格。当nextnotisnull均为True即块结束时。
    • 然后我用切块的末端位置,并采取差异......这给出了块的大小。
    • 如果块的最大尺寸比我们的阈值,然后返回True
  • which_ids
    • 使用列表中理解一个groupby对象上只
    • 返回groupby名所在组本身具有块大小超过我们的阈值。
+0

感谢您的解决方案,但它失败并给出错误。我确信我可以调整你的解决方案,但jezrael的工作很好。再次感谢! –