【问题标题】:Fill the "na" values with unique "na" identifier when doing pandas merge进行熊猫合并时,用唯一的“na”标识符填充“na”值
【发布时间】:2018-03-14 17:43:40
【问题描述】:

我想合并两个 pandas 数据框。

df1 = 
A   B
2   11
2   13
2   15
2   19
2   25
2   35
2   41
2   47
2   46
2   51
3   9
3   15
3   17
3   23
3   25
3   29
5   4
5   23
5   28

与另一个数据框。

   df2 = 
A   B    C
2   11   abc
2   13   cdd
2   35   cdd
2   41   cdd
2   47   cdd
3   9   cdd
3   15   cdd
3   17   cdd
3   23   cdd

两个数据帧都按“A”排序,然后按“B”排序。我想通过columns['A', 'B']合并;因此,对于缺少数据的“C”列,我想用na 填充它们,但对于na 的每个缺失块使用na_uniqueNumber

如何更新此合并方法:

data_frames = [df1, df2]
df_update = reduce(lambda left,right: pd.merge(
    left, right, on=['A', 'B'], how='outer'), data_frames).fillna('na')

注意:在其他列存在的情况下,代码应仅在“C”中使用唯一值更新na

预期输出:

   df2 = 
A   B    C
2   11   abc
2   13   cdd
2   15   na_01
2   19   na_01 
2   25   na_01  
2   35   cdd
2   41   cdd
2   47   cdd
2   46   na_02
2   51   na_02
3   9   cdd
3   15   cdd
3   17   cdd
3   23   cdd
3   25   na_03
3   29   na_03
5   4   na_04
5   23   na_04
5   28   na_04

谢谢,

【问题讨论】:

    标签: python pandas dataframe merge


    【解决方案1】:

    您可以先merge 两个DataFrames 通过左连接,然后为每个组A 计数NaNs,将其替换为fillna

    df = df1.merge(df2, how='left')
    
    isna = df['C'].isnull()
    count_nans =(isna.ne(isna.groupby(df['A']).shift()) & isna).cumsum().astype(str).str.zfill(2)
    df['C'] = df['C'].fillna('na_' + count_nans)
    print (df)
        A   B      C
    0   2  11    abc
    1   2  13    cdd
    2   2  15  na_01
    3   2  19  na_01
    4   2  25  na_01
    5   2  35    cdd
    6   2  41    cdd
    7   2  47    cdd
    8   2  46  na_02
    9   2  51  na_02
    10  3   9    cdd
    11  3  15    cdd
    12  3  17    cdd
    13  3  23    cdd
    14  3  25  na_03
    15  3  29  na_03
    16  5   4  na_04
    17  5  23  na_04
    18  5  28  na_04
    

    【讨论】:

      【解决方案2】:

      IIUC

      New = df_update[df_update.C == 'na']
      
      s=New.reset_index().groupby('A').apply(lambda x : x['index'].diff().ne(1)).cumsum()
      
      df_update.loc[df_update.C == 'na','C']+='_'+s.astype(str).str.pad(2,fillchar='0').values
      df_update
      Out[124]: 
          A   B      C
      0   2  11    abc
      1   2  13    cdd
      2   2  15  na_01
      3   2  19  na_01
      4   2  25  na_01
      5   2  35    cdd
      6   2  41    cdd
      7   2  47    cdd
      8   2  46  na_02
      9   2  51  na_02
      10  3   9    cdd
      11  3  15    cdd
      12  3  17    cdd
      13  3  23    cdd
      14  3  25  na_03
      15  3  29  na_03
      16  5   4  na_04
      17  5  23  na_04
      18  5  28  na_04
      

      【讨论】:

      • 由于某种原因,这不适用于我的数据。我试图看看是否有任何错别字,但还没有。这个错误信息有意义吗AttributeError: 'DataFrame' object has no attribute 'str'.
      • 由于某种原因这不起作用。 AttributeError: 'DataFrame' object has no attribute 'str'。我认为问题在于s.astype(str)
      • @everestial007 可能会更改为 s.apply(lambda x : x.astype(str))
      【解决方案3】:

      尝试 1

      def labels(d):
          mask = d.C.isnull().values
          a = d.A.values
          c = d.C.values.copy()
          i = np.flatnonzero(mask)
          f, u = pd.factorize([
              (a_, c_) for a_, c_ in zip(a[mask], (~mask).cumsum()[mask])
          ])
          c[i] = [f'na_{g+1:02d}' for g in f]
          return c
      
      
      df1.merge(df2, 'left').assign(C=labels)
      
          A   B      C
      0   2  11    abc
      1   2  13    cdd
      2   2  15  na_01
      3   2  19  na_01
      4   2  25  na_01
      5   2  35    cdd
      6   2  41    cdd
      7   2  47    cdd
      8   2  46  na_02
      9   2  51  na_02
      10  3   9    cdd
      11  3  15    cdd
      12  3  17    cdd
      13  3  23    cdd
      14  3  25  na_03
      15  3  29  na_03
      16  5   4  na_04
      17  5  23  na_04
      18  5  28  na_04
      

      尝试 2
      还有 Python 3.6

      def labeler():
          tracker = {}
          return lambda k: tracker.setdefault(k, len(tracker) + 1)
      
      def fill(d):
          c_ = labeler()
          return [
              f'na_{c_((a, g)):02d}' if pd.isna(c) else c
              for a, c, g in zip(d.A, d.C, d.C.notna().cumsum())
          ]
      
      df1.merge(df2, 'left').assign(C=fill)
      
          A   B      C
      0   2  11    abc
      1   2  13    cdd
      2   2  15  na_01
      3   2  19  na_01
      4   2  25  na_01
      5   2  35    cdd
      6   2  41    cdd
      7   2  47    cdd
      8   2  46  na_02
      9   2  51  na_02
      10  3   9    cdd
      11  3  15    cdd
      12  3  17    cdd
      13  3  23    cdd
      14  3  25  na_03
      15  3  29  na_03
      16  5   4  na_04
      17  5  23  na_04
      18  5  28  na_04
      

      尝试 3
      另一种选择。不知道我更喜欢什么。

      def labeler(d):
          mask = d.C.notna()
          csum = mask.cumsum()
          tups = list(zip(d.A, csum, d.C, ~mask))
          trac = dict(map(reversed, enumerate(
              pd.unique([t[:2] for t in tups if t[-1]]), 1
          )))
          return list(map(
              lambda t: f'na_{trac.get(t[:2]):02d}' if t[:2] in trac else t[2], tups
          ))
      
      df1.merge(df2, 'left').assign(C=labeler)
      
          A   B      C
      0   2  11    abc
      1   2  13  na_01
      2   2  15  na_01
      3   2  19  na_01
      4   2  25  na_01
      5   2  35    cdd
      6   2  41    cdd
      7   2  47  na_02
      8   2  46  na_02
      9   2  51  na_02
      10  3   9    cdd
      11  3  15    cdd
      12  3  17    cdd
      13  3  23  na_03
      14  3  25  na_03
      15  3  29  na_03
      16  5   4  na_04
      17  5  23  na_04
      18  5  28  na_04
      

      【讨论】:

      • 感谢@piRSquared。这些代码是基于python2.7 吗?
      • 不,3.6 因为 f 弦。但是您可以将其与str.format 一起使用。我正在尝试正确选择第二个选项
      • 是的,f-strings 说的是python 3.5 doesn't support 'f' prefix
      猜你喜欢
      • 2021-08-31
      • 1970-01-01
      • 1970-01-01
      • 2020-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-20
      相关资源
      最近更新 更多