2016-11-05 4 views
1

DataFrameにN/A値を選択的に入力したいと思います。特に、カラム内に一連の結果的なナンセンスがある場合、ナノシーケンスの長さが指定されたスレッショルドを下回っている場合にのみ、それらを先行するnon-nan値で埋めるようにします。例えば、閾値が3である場合、3以下のカラム内シーケンスは、先行する非ナノ値で満たされるが、4以上のシーケンスはそのまま残される。入力データフレームは、私は出力になりたいパンダでfillna()を選択的に使用する

2 5 4 
    nan nan nan 
    nan nan nan 
    5 nan nan 
    9 3 nan 
    7 9 1 

であれば、ある

2 5 4 
    2 5 nan 
    2 5 nan 
    5 5 nan 
    9 3 nan 
    7 9 1 

DATAFRAMEに適用fillna機能は、方法および制限のオプションがあります。しかし、これらは残念ながら課題を達成するには不十分です。私はmethod='ffill'limit=3を指定しようとしましたが、これは上記のように選択的ではなく、任意のシーケンスの最初の3つのナンバーを埋めます。

これは、いくつかの条件文を使って列ごとにコード化することができますが、何かもっとPythonicがあるはずです。これを達成するための効率的な方法に関する提案はありますか?

答えて

3

連続したグループで作業することは、パンダではやや厄介です。少なくとも、これを行うためのすてきな方法はわかりませんが、これはまったく同じことではありません。

In [68]: nulls = df.isnull() 
    ...: groups = (nulls != nulls.shift()).cumsum() 
    ...: to_fill = groups.apply(lambda x: x.groupby(x).transform(len) <= 3) 
    ...: df.where(~to_fill, df.ffill()) 
    ...: 
Out[68]: 
    0 1 2 
0 2.0 5.0 4.0 
1 2.0 5.0 NaN 
2 2.0 5.0 NaN 
3 5.0 5.0 NaN 
4 9.0 3.0 NaN 
5 7.0 9.0 1.0 

さて、それはあまりにもトリッキーだから、私は好きではない別の代替:あなたが欲しいものを得るために:-)

一つの方法は、比較-CUMSUM-GROUPBYパターンを用いることであろう:

def method_2(df): 
    nulls = df.isnull() 
    filled = df.ffill(limit=3) 
    unfilled = nulls & (~filled.notnull()) 
    nf = nulls.replace({False: 2.0, True: np.nan}) 
    do_not_fill = nf.combine_first(unfilled.replace(False, np.nan)).bfill() == 1 
    return df.where(do_not_fill, df.ffill()) 

これは、任意のgroupbyツールを使用していないので、高速にする必要があります。別のアプローチは、どの要素が長さ1,2、または3のグループであるため、手動で(シフトを使用して)どの要素を塗りつぶすかということです。

+0

@DSMありがとうございます。それは本当に素晴らしいソリューションです。私たちが探しているものを実際に提供しています。ちょうど1つのコメント:それはかなり遅いです。私は、サイズ530x11500のDataFrameには3の制限があり、それは約32秒かかりました。したがって、このソリューションは素晴らしいですが、実行時間を短縮する代替ソリューションが高く評価されています。 – splinter

+0

非常に高速です!ウォールタイム:9.01秒 – splinter

関連する問題