Python: как заменить NaN условиями в фрейме данных?
у меня есть фрейм данных df1
Это соответствует эгелисту nodes
в сети 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
я хочу добавить столбец w
, что соответствует value_j
если есть значение. Если value_j
is NaN
Я хотел бы задать 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]
6 ответов
можно использовать 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()]
я думаю, вам нужно fillna
через раз ffill
и bfill
и возьмите среднее значение этого тогда fillna
С 1
as:
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
Обновленный Ответ:
можно использовать groupby
и transform
найти mean
затем fillna
С 1
и использовать np.where
для заполнения значений w
as:
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
Я предполагаю, что "соседними узлами"i
, вы в конечном счете хотите среднее значение value_j
' s по всем строкам одного и того же i
.
в этом случае, мы можем использовать groupby
transform
С fillna
:
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'
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)
этот код может помочь вам. Пожалуйста, проверьте и дайте мне знать.
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
'''
С 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)))