如何使用pandas列中的前N个值来填充NaN?

时间:2018-01-18 10:04:06

标签: python python-3.x pandas dataframe

说我有一个时间序列数据如下。

 df
       priceA    priceB
  0     25.67    30.56
  1     34.12    28.43
  2     37.14    29.08
  3     Nan       34.23
  4     32          Nan
  5     18.75    41.1
  6     Nan       45.12
  7     23          39.67
  8     Nan       36.45
  9      36         Nan

现在,我想通过取列中前N个值的平均值来填充 priceA 列中的NaN。在这种情况下,取N = 3。 对于 priceB 列,我必须通过上面的M行填充Nan(当前索引-M)。

我尝试为它编写for循环,这不是一个好习惯,因为我的数据太大了。有更好的方法吗?

N=3
M=2
def fillPriceA( df,indexval,n):
      temp=[ ]
      for i in range(n):
          if i < 0:
                continue
          temp.append(df.loc[indexval-(i+1), 'priceA'])

      return np.nanmean(np.array(temp, dtype=np.float))

def fillPriceB(df,  indexval, m):
        return df.loc[indexval-m, 'priceB']

for idx, rows for df.iterrows():
         if idx< N: 
               continue
         else:
                if rows['priceA']==None:
                     rows['priceA']= fillPriceA(df, idx,N)
                if rows['priceB']==None:
                     rows['priceB']=fillPrriceB(df,idx,M)

预期产出:

        priceA      priceB
0      25.67        30.56
1      34.12        28.43
2      37.14        29.08
3      32.31        34.23
4      32             29.08
5      18.75       41.1
6       27.68      45.12
7       23            39.67
8       23.14      36.45
9       36            39.67

3 个答案:

答案 0 :(得分:2)

解决方案可能只适用于nan索引(请参阅dataframe boolean indexing):

param = dict(priceA = 3, priceB = 2) #Number of previous values to consider

for col in df.columns:
    for i in df[np.isnan(df[col])].index: #Iterate over nan index 
        _window = df.iloc[max(0,(i-param[col])):i][col] #get the nth expected elements
        df.loc[i][col] = _window.mean() if col == 'priceA' else _window.iloc[0] #Replace with right method

print(df)

结果:

      priceA  priceB
0  25.670000   30.56
1  34.120000   28.43
2  37.140000   29.08
3  32.310000   34.23
4  32.000000   29.08
5  18.750000   41.10
6  27.686667   45.12
7  23.000000   39.67
8  23.145556   36.45
9  36.000000   39.67

注意
1。使用np.isnan()表示您的列是数字的。如果之前没有使用pd.to_numeric()转换您的列:

...
for col in df.columns:
    df[col] = pd.to_numeric(df[col], errors = 'coerce')
    ...

或者使用pd.isnull()代替(请参阅下面的示例)。注意表演(numpy更快):

from random import randint

#A sample with 10k elements and some np.nan
arr = np.random.rand(10000)
for i in range(100):
    arr[randint(0,9999)] = np.nan

#Performances
%timeit pd.isnull(arr)
10000 loops, best of 3: 24.8 µs per loop

%timeit np.isnan(arr)
100000 loops, best of 3: 5.6 µs per loop

2。更通用的替代方法可以是定义方法和窗口大小以应用dict中的每一列:

import pandas as pd

param = {}
param['priceA'] = {'n':3,
                   'method':lambda x: pd.isnull(x)}

param['priceB'] = {'n':2,
                   'method':lambda x: x[0]}

param现在包含n元素数量和method lambda表达式。因此,重写你的循环:

for col in df.columns:
    for i in df[np.isnan(df[col])].index: #Iterate over nan index 
        _window = df.iloc[max(0,(i-param[col]['n'])):i][col] #get the nth expected elements
        df.loc[i][col] = param[col]['method'](_window.values) #Replace with right method

print(df)#This leads to a similar result.

答案 1 :(得分:1)

您可以使用NA遮罩来完成每列所需的操作:

import pandas as pd
import numpy as np

df = pd.DataFrame({'a': [1,2,3,4, None, 5, 6], 'b': [1, None, 2, 3, 4, None, 7]})
df

#     a b
# 0 1.0 1.0
# 1 2.0 NaN
# 2 3.0 2.0
# 3 4.0 3.0
# 4 NaN 4.0
# 5 5.0 NaN
# 6 6.0 7.0

for col in df.columns:
    s = df[col]
    na_indices = s[s.isnull()].index.tolist()
    prev = 0
    for k in na_indices:
        s[k] = np.mean(s[prev:k])
        prev = k

    df[col] = s

print(df)

    a   b
# 0 1.0 1.0
# 1 2.0 1.0
# 2 3.0 2.0
# 3 4.0 3.0
# 4 2.5 4.0
# 5 5.0 2.5
# 6 6.0 7.0

虽然这仍然是一个自定义操作,但我很确定它会稍微快一点,因为它不会遍历每一行,只是超过NA值,我假设它与实际数据相比将是稀疏的

答案 2 :(得分:0)

要填写价格,请使用rolling,然后使用shift并在fillna中使用此结果,

# make some data
df = pd.DataFrame({'priceA': range(10)})

#make some rows missing
df.loc[[4, 6], 'priceA'] = np.nan

n = 3

df.priceA = df.priceA.fillna(df.priceA.rolling(n, min_periods=1).mean().shift(1))

这里唯一的边缘情况是两个nans在彼此的n范围内,但它似乎在你的问题中处理这个。

对于priceB,只需使用shift

df = pd.DataFrame({'priceB': range(10)})
df.loc[[4, 8], 'priceB'] = np.nan

m = 2

df.priceB = df.priceB.fillna(df.priceB.shift(m))

和以前一样,有一个边缘情况,在另一个纳米之前存在一个完全为m的纳米。