置換 - python nan代入




複数の日数のデータがない場合、NaNでデータフレームを埋める (3)

私は毎日のデータフレームを得るために補間するパンダのデータフレームを持っています。 元のデータフレームは次のようになります。

               col_1      vals 
2017-10-01  0.000000  0.112869 
2017-10-02  0.017143  0.112869 
2017-10-12  0.003750  0.117274 
2017-10-14  0.000000  0.161556 
2017-10-17  0.000000  0.116264   

補間されたデータフレームでは、日付のギャップが5日を超えるNaNにデータ値を変更する必要があります。 たとえば、上記のデータフレームでは、 2017-10-022017-10-12間のギャップが5日を超えているため、補間されたデータフレームでは、これら2つの日付間のすべての値を削除する必要があります。 私はこれを行う方法がわからない、おそらくcombine_first

- EDIT:補間されたデータフレームは次のようになります:

            col_1      vals 
2017-10-01  0.000000  0.112869 
2017-10-02  0.017143  0.112869 
2017-10-03  0.015804  0.113309 
2017-10-04  0.014464  0.113750 
2017-10-05  0.013125  0.114190 
2017-10-06  0.011786  0.114631 
2017-10-07  0.010446  0.115071 
2017-10-08  0.009107  0.115512 
2017-10-09  0.007768  0.115953 
2017-10-10  0.006429  0.116393 
2017-10-11  0.005089  0.116834 
2017-10-12  0.003750  0.117274 
2017-10-13  0.001875  0.139415 
2017-10-14  0.000000  0.161556 
2017-10-15  0.000000  0.146459 
2017-10-16  0.000000  0.131361 
2017-10-17  0.000000  0.116264

期待される出力:

               col_1      vals
2017-10-01  0.000000  0.112869
2017-10-02  0.017143  0.112869
2017-10-12  0.003750  0.117274
2017-10-13  0.001875  0.139415
2017-10-14  0.000000  0.161556
2017-10-15  0.000000  0.146459
2017-10-16  0.000000  0.131361
2017-10-17  0.000000  0.116264

これは、あなたの望むことですか?

data0 = """2017-10-01  0.000000  0.112869 
2017-10-02  0.017143  0.112869 
2017-10-12  0.003750  0.117274 
2017-10-14  0.000000  0.161556 
2017-10-17  0.000000  0.116264"""
data = [row.split('  ') for row in data0.split('\n')]

df = pd.DataFrame(data, columns = ['date','col_1','vals'])
df.date = pd.to_datetime(df.date)
last_observation = df.assign(last_observation = df.date.diff().dt.days)
df.set_index(['date'], inplace = True)

all_dates = pd.date_range(start = last_observation.date.min(), 
                          end = last_observation.date.max())
df_interpolated = df.reindex(all_dates).astype(np.float64).interpolate()
df_interpolated = df_interpolated.join(last_observation.set_index('date').last_observation)
df_interpolated['discard'] = (df_interpolated.last_observation.bfill() > 5) & df_interpolated.last_observation.isnull()
df_interpolated[['col_1','vals']] = df_interpolated[['col_1','vals']].where(~df_interpolated.discard)

出力は次のとおりです。

               col_1      vals  last_observation  discard
2017-10-01  0.000000  0.112869               NaN    False
2017-10-02  0.017143  0.112869               1.0    False
2017-10-03       NaN       NaN               NaN     True
2017-10-04       NaN       NaN               NaN     True
2017-10-05       NaN       NaN               NaN     True
2017-10-06       NaN       NaN               NaN     True
2017-10-07       NaN       NaN               NaN     True
2017-10-08       NaN       NaN               NaN     True
2017-10-09       NaN       NaN               NaN     True
2017-10-10       NaN       NaN               NaN     True
2017-10-11       NaN       NaN               NaN     True
2017-10-12  0.003750  0.117274              10.0    False
2017-10-13  0.001875  0.139415               NaN    False
2017-10-14  0.000000  0.161556               2.0    False
2017-10-15  0.000000  0.146459               NaN    False
2017-10-16  0.000000  0.131361               NaN    False
2017-10-17  0.000000  0.116264               3.0    False

アイデアは最初に補間を生成してから、どの観測値を落とすかを決定するというものです。 まず、現在の観測と最後の観測の間の日数を割り当てます。 この数値が5を超える項目、およびそれ以前の項目を破棄したいので、 .bfillを使用して、この数値を前の補間に割り当ててから、5と比較します。ただし、廃棄の決定を.bfill場合は、これはあなたが望まないものです。 したがって、観測を破棄しないという条件を含める必要があります。これは、 last_observation列の.notnull()メソッドで確認します。

最後に.whereメソッドを使用して、破棄基準を満たさないエントリを保持します。 デフォルトでは、他はNAsに置き換えられます。


まずギャップが5日を超えた場所を特定したいと思います。 そこから、そのようなギャップの間にグループを特定する配列を生成します。 最後に、 groupbyを使用して毎日の頻度にgroupbyて補間します。

# convenience: assign string to variable for easier access
daytype = 'timedelta64[D]'

# define five days for use when evaluating size of gaps
five = np.array(5, dtype=daytype)

# get the size of gaps
deltas = np.diff(df.index.values).astype(daytype)

# identify groups between gaps
groups = np.append(False, deltas > five).cumsum()

# handy function to turn to daily frequency and interpolate
to_daily = lambda x: x.asfreq('D').interpolate()

# and finally...
df.groupby(groups, group_keys=False).apply(to_daily)

               col_1      vals
2017-10-01  0.000000  0.112869
2017-10-02  0.017143  0.112869
2017-10-12  0.003750  0.117274
2017-10-13  0.001875  0.139415
2017-10-14  0.000000  0.161556
2017-10-15  0.000000  0.146459
2017-10-16  0.000000  0.131361
2017-10-17  0.000000  0.116264

あなた自身の補間方法を提供したい場合は、 上記のように次のように変更することができます:

daytype = 'timedelta64[D]'
five = np.array(5, dtype=daytype)
deltas = np.diff(df.index.values).astype(daytype)
groups = np.append(False, deltas > five).cumsum()

# custom interpolation function that takes a dataframe
def my_interpolate(df):
    """This can be whatever you want.
    I just provided what will result
    in the same thing as before."""
    return df.interpolate()

to_daily = lambda x: x.asfreq('D').pipe(my_interpolate)

df.groupby(groups, group_keys=False).apply(to_daily)

               col_1      vals
2017-10-01  0.000000  0.112869
2017-10-02  0.017143  0.112869
2017-10-12  0.003750  0.117274
2017-10-13  0.001875  0.139415
2017-10-14  0.000000  0.161556
2017-10-15  0.000000  0.146459
2017-10-16  0.000000  0.131361
2017-10-17  0.000000  0.116264

行間に5日以上の間隔がある2つのブロックを持つために、私はあなたの例にいくつかの行を追加しました。
2つのテーブルを.csvファイルとしてローカルに保存し、最初の列名としてdateを追加して、以下のマージを完了します。

セットアップ

import pandas as pd
import numpy as np
df_1=pd.read_csv('df_1.csv', delimiter=r"\s+")
df_2=pd.read_csv('df_2.csv', delimiter=r"\s+")

2つのデータセットをマージ(結合)し、列の名前を変更します。
5日間のギャップを持つ2つのグループに気付く。

df=df_2.merge(df_1, how='left', on='Date').reset_index(drop=True)
df.columns=['date','col','val','col_na','val_na']    #purely aesthetic

df

    date        col         val         col_na      val_na
0   2017-10-01  0.000000    0.112869    0.000000    0.112869
1   2017-10-02  0.017143    0.112869    0.017143    0.112869
2   2017-10-03  0.015804    0.113309    NaN         NaN
3   2017-10-04  0.014464    0.113750    NaN         NaN
4   2017-10-05  0.013125    0.114190    NaN         NaN
5   2017-10-06  0.011786    0.114631    NaN         NaN
6   2017-10-07  0.010446    0.115071    NaN         NaN
7   2017-10-08  0.009107    0.115512    NaN         NaN
8   2017-10-09  0.007768    0.115953    NaN         NaN
9   2017-10-10  0.006429    0.116393    NaN         NaN
10  2017-10-11  0.005089    0.116834    NaN         NaN
11  2017-10-12  0.003750    0.117274    0.003750    0.117274
12  2017-10-13  0.001875    0.139415    NaN         NaN
13  2017-10-14  0.000000    0.161556    0.000000    0.161556
14  2017-10-15  0.000000    0.146459    NaN         NaN
15  2017-10-16  0.000000    0.131361    NaN         NaN
16  2017-10-17  0.000000    0.989999    0.000000    0.116264
17  2017-10-18  0.000000    0.412311    NaN         NaN
18  2017-10-19  0.000000    0.166264    NaN         NaN
19  2017-10-20  0.000000    0.123464    NaN         NaN
20  2017-10-21  0.000000    0.149767    NaN         NaN
21  2017-10-22  0.000000    0.376455    NaN         NaN
22  2017-10-23  0.000000    0.000215    NaN         NaN
23  2017-10-24  0.000000    0.940219    NaN         NaN
24  2017-10-25  0.000000    0.030352    0.000000    0.030352
25  2017-10-26  0.000000    0.111112    NaN         NaN
26  2017-10-27  0.000000    0.002500    NaN         NaN

タスクを実行する方法

def my_func(my_df):
    non_na_index=[]                                      #define empty list
    for i in range(len(my_df.iloc[:,[1]])):
        if not pd.isnull(my_df.iloc[i,[3]][0]):
            non_na_index.append(i)                       #add indexes of rows that that have non NaN value  
    sub=np.roll(non_na_index, shift=-1)-non_na_index     #subract column in indexes to find row count of NaN   
    sub=sub[:-1]                                         #get rid of last element (calculation artifact)
    for i in reversed(range(len(sub))):
        if sub[i]>=5:                       #identidy indexes with more than 5 NaN in between
            b=non_na_index[i+1]             #assign end index
            a=non_na_index[i]+1             #assign start index
            my_df=my_df.drop(my_df.index[[range(a,b)]])  #drop the rows within the range
    return(my_df)

dfを使って関数を実行する

new_df=my_func(df)
new_df=df.drop(['col_na','val_na'],1)    # drop the two extra columns
new_df

    date        col         val
0   2017-10-01  0.000000    0.112869
1   2017-10-02  0.017143    0.112869
11  2017-10-12  0.003750    0.117274
12  2017-10-13  0.001875    0.139415
13  2017-10-14  0.000000    0.161556
14  2017-10-15  0.000000    0.146459
15  2017-10-16  0.000000    0.131361
16  2017-10-17  0.000000    0.989999
24  2017-10-25  0.000000    0.030352
25  2017-10-26  0.000000    0.111112
26  2017-10-27  0.000000    0.002500