2016-02-15 3 views
1

У меня есть кадр данных pandas, из которых один столбец является датой, а другой содержит некоторое значение вместе с NaN. Пример, кадр данных приводится ниже -Замените NaN с некоторым кратным предыдущему значению в Pandas Dataframe

import pandas as pd 
import numpy as np 
df = pd.DataFrame({'a':pd.date_range(start="01/01/2013",periods=6, freq="D"),'b': [1,np.nan,np.nan,4,np.nan,7]}) 
df 
a   b 
2013-01-01 1 
2013-01-02 NaN 
2013-01-03 NaN 
2013-01-04 4 
2013-01-05 NaN 
2013-01-05 7 

Теперь я хочу, чтобы заполнить эту пренебрежимо малых с некоторым фиксированным множителем предыдущего значения, то есть выше кадра данных будет выглядеть следующим образом после преобразования, если фиксированный множитель 0.5-

a   b 
2013-01-01 1 
2013-01-02 0.5 
2013-01-03 0.25 
2013-01-04 4 
2013-01-05 2 
2013-01-05 7 

Один из способ сделать это было бы петлю над б, а затем использовать .loc функцию, чтобы преобразовать его, то есть мы можем использовать ниже данный фрагмент кода

for i in range(df.shape[0]): 
    if np.isnan(df.loc[i, 'b']): 
     df.loc[i, 'b'] = df.loc[i-1, 'b']*0.5 

Хотя это хорошо работает, но оно плохо масштабируется - требуется много времени. Таким образом, было бы очень полезно, если бы кто-то помог мне сделать это более эффективно, используя встроенную функцию pandas.

Спасибо!

+0

Ваш код не работает, как он перепишет значения 'не являющихся NaN' с' 1' – EdChum

+0

в результате фрагмент отличается от ожидаемого DataFrame после преобразования, пожалуйста, укажите ожидаемый DataFrame. –

+1

Основываясь на вашем описании, почему не вторая в последней строке '2'? – EdChum

ответ

1

Я не думаю, что вы можете сделать это в vectorised образом, ваш цикл зависит от ДФ модифицируется на месте в то время как цикл, вы можете ограничить работу на только строки, которые содержат NaN:

In [32]: 
while df['b'].isnull().any(): 
    df.loc[df['b'].isnull(),'b'] = df['b'].shift() /2 
df 

Out[32]: 
      a  b 
0 2013-01-01 1.00 
1 2013-01-02 0.50 
2 2013-01-03 0.25 
3 2013-01-04 4.00 
4 2013-01-05 2.00 
5 2013-01-06 7.00 

тайминги

In [36]: 
%%timeit 
for i in range(df.shape[0]): 
    if np.isnan(df.loc[i, 'b']): 
     df.loc[i, 'b'] = df.loc[i-1, 'b']*0.5 
1000 loops, best of 3: 1.27 ms per loop 

In [40]: 
%%timeit 
while df['b'].isnull().any(): 
    df.loc[df['b'].isnull(),'b'] = df['b'].shift() /2 

10000 loops, best of 3: 91 µs per loop 
+0

Только что проверенный, этот довольно быстрый даже для петлевого решения. Ницца! С моим основанным на NumPy узким местом, кажется, является последний шаг, на котором он пытается обновить столбец dataframe с результатом, полученным NumPy. – Divakar

1

Существует зависимость здесь можно проследить с помощью комбинации diff + cumprod для векторизованного раствора. Философия будет очень похожа на решение на основе diff + cumsum для проблемы MATLAB, подробно рассмотренное here. Единственное изменение здесь заключается в том, что мы имеем дело с умножением вместо суммирования. Реализация будет выглядеть примерно так -

# Input param 
multiplier = 0.5 

# Store reciprocal of multiplier 
reci_multiplier = 1.0/multiplier 

# Store the relevant column as a numpy array for further computations 
df_arr = np.array(df['b']) 

# Get nan mask for the input column 
nan_mask = np.isnan(df_arr) 

# Indices where the "shifts" occur (basically are the non NaN positions) 
idx = np.where(~nan_mask)[0] 

# Set nan positions with multiplier 
df_arr[nan_mask] = multiplier 

# Scale non-nan positions with "position based multipliers" , which when 
# "cumprod-ed" would result in our desired output. Update column in df. 
df_arr[idx[1:]] /= df_arr[idx[:-1]]*(reci_multiplier**(-(np.diff(idx)-1))) 
df['b'] = df_arr.cumprod() 

ввод пробы, выходной -

Input dataframe: 
      a b 
0 2013-01-01 5 
1 2013-01-02 NaN 
2 2013-01-03 NaN 
3 2013-01-04 4 
4 2013-01-05 NaN 
5 2013-01-06 NaN 
6 2013-01-07 NaN 
7 2013-01-08 NaN 
8 2013-01-09 8 
9 2013-01-10 3 
10 2013-01-11 NaN 
11 2013-01-12 11 

multiplier = 0.4 # Note that this is different from the original 0.5 
        # to show some variety with the application of solution 
Output dataframe: 
      a  b 
0 2013-01-01 5.0000 
1 2013-01-02 2.0000 
2 2013-01-03 0.8000 
3 2013-01-04 4.0000 
4 2013-01-05 1.6000 
5 2013-01-06 0.6400 
6 2013-01-07 0.2560 
7 2013-01-08 0.1024 
8 2013-01-09 8.0000 
9 2013-01-10 3.0000 
10 2013-01-11 1.2000 
11 2013-01-12 11.0000 
+0

Должен ли 3-й ряд быть 1.000, а 5-я строка - 2.000? – EdChum

+0

@EdChum Ну, в моем примере ввода, вывода Я использую множитель '0,4', чтобы показать некоторое разнообразие там. – Divakar

+0

Ah OK, я видел ваш код, но не видел ваших входов – EdChum

Смежные вопросы