2017-07-16 6 views
2

iterrows()を削除するにはどうすればよいですか?これは、numpyやpandasですばやく行うことができますか?パンダ:ディクショナリ値によって設定された制限まで列の値を割り当てます

import pandas as pd 
import numpy as np 
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(), 
        'B': 'one one two three two two one three'.split(), 
        'C': np.arange(8)*0 }) 
print(df) 
#  A  B C 
# 0 foo one 0 
# 1 bar one 0 
# 2 foo two 0 
# 3 bar three 0 
# 4 foo two 0 
# 5 bar two 0 
# 6 foo one 0 
# 7 foo three 0 

selDict = {"foo":2, "bar":3} 

これは動作します:

for i, r in df.iterrows(): 
    if selDict[r["A"]] > 0: 
     selDict[r["A"]] -=1   
     df.set_value(i, 'C', 1) 

    print df 
#  A  B C 
# 0 foo one 1 
# 1 bar one 1 
# 2 foo two 1 
# 3 bar three 1 
# 4 foo two 0 
# 5 bar two 1 
# 6 foo one 0 
# 7 foo three 0 

答えて

2

ここに1つのアプローチだ -

1)ヘルパー機能:

def argsort_unique(idx): 
    # Original idea : http://stackoverflow.com/a/41242285/3293881 by @Andras 
    n = idx.size 
    sidx = np.empty(n,dtype=int) 
    sidx[idx] = np.arange(n) 
    return sidx 

def get_bin_arr(grplens, stop1_idx): 
    count_stops_corr = np.minimum(stop1_idx, grplens) 

    limsc = np.maximum(grplens, count_stops_corr) 
    L = limsc.sum() 

    starts = np.r_[0,limsc[:-1].cumsum()] 

    shift_arr = np.zeros(L,dtype=int) 
    stops = starts + count_stops_corr 
    stops = stops[stops<L] 

    shift_arr[starts] += 1 
    shift_arr[stops] -= 1 
    bin_arr = shift_arr.cumsum() 
    return bin_arr 

おそらく速い代替愚かスライスベースのヘルパー関数を持つ:

def get_bin_arr(grplens, stop1_idx): 
    stop1_idx_corr = np.minimum(stop1_idx, grplens)  
    clens = grplens.cumsum() 
    out = np.zeros(clens[-1],dtype=int)  
    out[:stop1_idx_corr[0]] = 1 
    for i,j in zip(clens[:-1], clens[:-1] + stop1_idx_corr[1:]): 
     out[i:j] = 1 
    return out 

2)主な機能:

def out_C(A, selDict): 
    k = np.array(selDict.keys()) 
    v = np.array(selDict.values()) 
    unq, C = np.unique(A, return_counts=1) 
    sidx3 = np.searchsorted(unq, k) 
    lims = np.zeros(len(unq),dtype=int) 
    lims[sidx3] = v 
    bin_arr = get_bin_arr(C, lims) 
    sidx2 = A.argsort() 
    out = bin_arr[argsort_unique(sidx2)]  
    return out 

サンプルの実行 -

オリジナルのアプローチ:

def org_app(df, selDict): 
    df['C'] = 0 
    d = selDict.copy()  
    for i, r in df.iterrows(): 
     if d[r["A"]] > 0: 
      d[r["A"]] -=1   
      df.set_value(i, 'C', 1) 
    return df 

ケース#1:

>>> df = pd.DataFrame({'A': 'foo bar foo bar res foo bar res foo foo res'.split()}) 
>>> selDict = {"foo":2, "bar":3, "res":1} 
>>> org_app(df, selDict) 
     A C 
0 foo 1 
1 bar 1 
2 foo 1 
3 bar 1 
4 res 1 
5 foo 0 
6 bar 1 
7 res 0 
8 foo 0 
9 foo 0 
10 res 0 
>>> out_C(df.A.values, selDict) 
array([1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0]) 

ケース#2:

>>> selDict = {"foo":20, "bar":30, "res":10} 
>>> org_app(df, selDict) 
     A C 
0 foo 1 
1 bar 1 
2 foo 1 
3 bar 1 
4 res 1 
5 foo 1 
6 bar 1 
7 res 1 
8 foo 1 
9 foo 1 
10 res 1 
>>> out_C(df.A.values, selDict) 
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
+0

これは役に立ちますか?私の考えは、dupsとlimitを使用することでした..これはランクを使用します。擬似コード。 "1"を適用し、ランクの上に "0"を返します。 https://stackoverflow.com/questions/14671013/ranking-of-numpy-array-with-possible-duplicates – Merlin

+0

@Merlinうーん、 'rankdata'があなたを助けてくれるとは思わないでください。また、そのような適用メソッドは、本質的にループしています。少数の 'keys'を扱っているのであれば、偽のメソッド/ applyが良い選択かもしれません。私は大量のデータとまともなデータを想定していた。キーの。 – Divakar

+0

@Merlin良いキャッチ!編集されました。 – Divakar

4

私が正しく理解している場合、あなたはcumcountを使用することができます。

df['C'] = (df.groupby('A').cumcount() < df['A'].map(selDict)).astype('int') 

df 
Out: 
    A  B C 
0 foo one 1 
1 bar one 1 
2 foo two 1 
3 bar three 1 
4 foo two 0 
5 bar two 1 
6 foo one 0 
7 foo three 0 
+0

@Merlinうまくベクトル化されているので、iterrowsよりも速いですが、pandasはNaNやdtypesのような多くの異なるものを扱いますので、numpyほど速くはありません。しかし、どのように確信しています。 Divakarを待ってみましょう。 :) – ayhan

+0

私を魅了する上で細かいトリッキー! ;)まあ、私よりもはるかに努力しました。 – Divakar

+0

@Divakarうん、それも複雑になるとは思っていませんでした。しかし、私はもっと学ぶことが大切です。 :) – ayhan

1

scipy.stats.rankdataはここに助けることができます。その後、我々はちょうどdf.A.map(selDict)と比較

>>> from scipy.stats import rankdata as rd 
>>> rd(df.A, 'ordinal') - rd(df.A, 'min') 
array([0, 0, 1, 1, 2, 2, 3, 4]) 

:そのバケット内の各要素のランクを導出するために、我々は「分」と「序」の方法の違いを取る

df.C = (rd(df.A, 'ordinal') - rd(df.A, 'min') < df.A.map(selDict)).astype(int) 

これを(rankdataを2回呼び出すこと)が少し効率が悪いかもしれませんが、scipyで最適化されたルーチンを使用するとそれを補うはずです。

あなたが「分」方式のためuniquebincountを使用して、「序」の方法と私の解決策のためのargsort()を繰り返し使用することができますscipyのダウンロードあなたが使用できない場合:次に、上記のようにdf.A.map(selDict)に比較

>>> _, v = np.unique(df.A, return_inverse=True) 
>>> df.A.argsort().argsort() - (np.cumsum(np.concatenate(([0], np.bincount(v)))))[v] 
0 0 
1 0 
2 1 
3 1 
4 2 
5 2 
6 3 
7 4 
Name: A, dtype: int64 

を。

関連する問題