【问题标题】:Python: how to replace NaN with conditions in a dataframe?Python:如何用数据框中的条件替换 NaN?
【发布时间】:2019-02-12 15:45:47
【问题描述】:

我有一个数据框 df1 对应于网络中 nodes 的 egelist 和节点本身的 value,如下所示:

df
    node_i    node_j    value_i   value_j
0    3         4          89         33
1    3         2          89         NaN
2    3         5          89         69
3    0         2          45         NaN
4    0         3          45         89
5    1         2          109        NaN
6    1         8          109        NaN

如果有值,我想添加对应于value_j 的列w。如果value_jNaN 我想将w 设置为i 的相邻节点值的平均值。在node_i只有相邻节点的情况下,NaN的值设置为w=1

所以最终的数据框应该是这样的:

df
    node_i    node_j    value_i   value_j      w
0    3         4          89         33       33
1    3         2          89         NaN      51      # average of adjacent nodes
2    3         5          89         69       69
3    0         2          45         NaN      89      # average of adjacent nodes
4    0         3          45         89       89
5    1         2          109        NaN       1      # 1
6    1         8          109        NaN       1      # 1

我正在执行如下循环,但我想使用 apply:

nodes = pd.unique(df['node_i'])
df['w'] = 0
for i in nodes:
    tmp = df[df['node_i'] == i]
    avg_w = np.mean(tmp['value_j'])
    if np.isnan(avg_w):
          df['w'][idx] = 1
    else:
          tmp.ix[tmp.value_j.isnull(), 'value_j'] = avg_w ## replace NaN with values
          df['w'][idx] = tmp['value_j'][idx]  

【问题讨论】:

  • 第 1 行和第 3 行的相邻节点是什么?
  • @sobek 第1行node_i的相邻节点是df['node_j'][1] = 2,而第3行node_i的相邻节点是df['node_j'][3] = 2
  • 不清楚你的意思是相邻节点的平均值
  • @sobek 它们是node_i 的相邻节点。比如node_i=3的相邻节点是[4,2,5]node_i=0的相邻节点是[2,3]node_i=1的相邻节点是[2,8]

标签: python pandas apply


【解决方案1】:

您可以使用groupby 来执行此操作:

fill_value = df.groupby("node_i")["value_j"].mean().fillna(1.0)
df["w"] = fill_value.reindex(df["node_i"]).values
df["w"][df["value_j"].notnull()] = df["value_j"][df["value_j"].notnull()]

【讨论】:

    【解决方案2】:

    我认为您需要 fillna 使用一次 ffillbfill 并取其平均值,然后将 fillna1 作为:

    df['w'] = ((df['value_j'].fillna(method='ffill')+df['value_j'].fillna(method='bfill'))/2).fillna(1).astype(int)
    
    df
        node_i  node_j  value_i value_j w
    0   3       4       89      33.0    33
    1   3       2       89      NaN     51
    2   3       5       89      69.0    69
    3   0       2       45      NaN     79
    4   0       3       45      89.0    89
    5   1       2       109     NaN     1
    6   1       8       109     NaN     1
    

    更新答案:

    您可以使用groupbytransform 找到mean 然后fillna1 并使用np.wherew 的值填充为:

    values = df.groupby('node_i')['value_j'].transform('mean').fillna(1)
    df['w'] = np.where(df['value_j'].notnull(),df['value_j'],values).astype(int)
    
    df
    
        node_i  node_j  value_i value_j w
    0   3       4       89      33.0    33
    1   3       2       89      NaN     51
    2   3       5       89      69.0    69
    3   0       2       45      NaN     89
    4   0       3       45      89.0    89
    5   1       2       109     NaN     1
    6   1       8       109     NaN     1
    

    【讨论】:

    • 一个问题:如何查看与node_i的对应关系?
    • 这个逻辑在一般情况下肯定行不通。如果 node_i = 3 有 3 个非空值和一个 NaN 值,您将完全忽略连续的非空值之一,并且仍然只平均两个 NaN 相邻值。
    【解决方案3】:

    我猜测,通过 i 的“相邻节点”,您最终需要同一 i 的所有行中 value_j 的平均值。

    在这种情况下,我们可以使用groupby transformfillna

    means = df.groupby('i')['value_j'].transform(np.mean)
    # this gives the correct values for w in the rows where value_j is null,
    # except when all the adjacent nodes have null value_j  (in which case it's still null)
    filled_means = means.fillna(1)
    # this corrects the last problem
    df['w'] = df['value_j'].fillna(filled_means)
    # this copies value_j, and fills the null rows with the corresponding rows from filled_means
    # and assigns it to the column 'w'
    

    【讨论】:

      【解决方案4】:

      apply 可能不是最快的解决方案,但既然这是您的要求,您可以这样进行:

      def nan_with_cond(row):
          if ~np.isnan(row['value_j']) : return row['value_j']
          else: return df.loc[df['node_i'] == row['node_i'], 'value_j'].mean()
      
      df['w'] = df.apply(nan_with_cond , axis=1).fillna(1)
      

      或仅使用apply:

      def nan_with_cond(row):
          if ~np.isnan(row['value_j']) : return row['value_j']
          else: 
              value = df.loc[df['node_i'] == row['node_i'], 'value_j'].mean()
              if np.isnan(value) : return 1
              else : return value
      
      df['w'] = df.apply(nan_with_cond , axis=1)
      

      【讨论】:

        【解决方案5】:

        此代码可能会对您有所帮助。请检查并告诉我。

        import pandas as pd
        import numpy as np
        
        df = pd.DataFrame({
                'node_i':[3,3,3,0,0,1,1],
                'node_j':[4,2,5,2,3,2,8],
                'value_i':[89,89,89,45,45,109,109],
                'value_j':[33,np.nan,69,np.nan,89,np.nan,np.nan]
        
                })
        
        wList = []
        
        for index,value in enumerate(df['value_j'].values):
            curValueNode_i = df.iloc[index,0]
            if not np.isnan(value):
                wList.append(value)
            else:
                checkIfNaNReturns = len(df[(df['node_i']==curValueNode_i) & (df['value_j'].notnull())].iloc[:,3].values)
                if (checkIfNaNReturns>0):
                    meanValue = np.mean(df[(df['node_i']==curValueNode_i) & (df['value_j'].notnull())].iloc[:,3].values)
                    wList.append(meanValue)
                else:
                    wList.append(1)
        
        df['w'] = wList
        print(df)
        
        
        '''
        Output of the code:
        
           node_i  node_j  value_i  value_j     w
        0       3       4       89     33.0  33.0
        1       3       2       89      NaN  51.0
        2       3       5       89     69.0  69.0
        3       0       2       45      NaN  89.0
        4       0       3       45     89.0  89.0
        5       1       2      109      NaN   1.0
        6       1       8      109      NaN   1.0
        
        '''
        

        【讨论】:

          【解决方案6】:

          最后一行带有 apply 和附加语句

          def func(x):
              if len(x)==1:
                  if math.isnan(x[0]):
                      return 1
                  else:
                      return x[0]
              elif not math.isnan(x[1]):
                  return x[1]
              elif len(x)==2:
                  return 1
              elif math.isnan(x[0]) or math.isnan(x[2]):
                  return 1
              else:
                  return (x[0]+x[2])/2
          
          
          
          df['Output']=df['value_j'].rolling(window=3, min_periods=0).apply(lambda x: func(x), raw=True).shift(-1)
          df['Output'].iloc[-1]=func(list(df['value_j'].tail(2)))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-24
            • 2017-12-05
            • 1970-01-01
            • 2021-11-06
            • 1970-01-01
            • 1970-01-01
            • 2022-01-01
            相关资源
            最近更新 更多