シリーズ/データフレーム列のパンダ条件付き作成


314

以下の行に沿ってデータフレームがあります:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

データフレームと同じ長さの別の列をデータフレームに追加(またはシリーズを生成)したい(=レコード/行の等しい数)。 。

これを行う最良の方法は何ですか?

回答:


712

選択肢が2つしかない場合:

df['color'] = np.where(df['Set']=='Z', 'green', 'red')

例えば、

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)

収量

  Set Type  color
0   Z    A  green
1   Z    B  green
2   X    B    red
3   Y    C    red

3つ以上の条件がある場合は、を使用しますnp.select。たとえば、あなたがしたい場合colorであることを

  • yellow いつ (df['Set'] == 'Z') & (df['Type'] == 'A')
  • そうでないblueとき(df['Set'] == 'Z') & (df['Type'] == 'B')
  • そうでないpurpleとき(df['Type'] == 'B')
  • そうでなければblack

次に使用します

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
conditions = [
    (df['Set'] == 'Z') & (df['Type'] == 'A'),
    (df['Set'] == 'Z') & (df['Type'] == 'B'),
    (df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)

これは

  Set Type   color
0   Z    A  yellow
1   Z    B    blue
2   X    B  purple
3   Y    C   black

1
andを使用してwhere句に2つの条件を配置すると機能しません
Amol Sharma

2
df ['color'] = list(np.where(df ['Set'] == 'Z'、 'green'、 'red'))はパンダの警告を抑制します:値がコピーに設定されようとしていますDataFrameからのスライスの。代わりに.loc [row_indexer、col_indexer] = valueを使用してみてください
denson

3
「緑」と「赤」も列演算に置き換えることができます。 例えばdf['foo'] = np.where(df['Set']=='Z', df['Set'], df['Type'].shift(1))
アレハンドロ

np.whereは新しい列を作成しますか?私はこのコードを使用し、df.color.head()を実行すると、「numpy.ndarray」オブジェクトに属性「head」がありません
vvv

3
何度も賛成できないのは残念です。1つの賛成投票では十分ではないようです。
Harper、

120

リスト内包表記は、条件付きで別の列を作成する別の方法です。例のように、オブジェクトのdtypeを列で操作している場合、リスト内包表記は通常、他のほとんどのメソッドよりも優れています。

リスト内包表記の例:

df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]

%timeitテスト:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop

4
データフレームがはるかに大きい場合(-sizeと考えるpd.DataFrame({'Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000}))はnumpy.whereペースmapが上がりますが、リストの理解度は非常に優れています(約50%速いnumpy.where)。
ブラックサイト2017

3
条件が複数の列からの情報を必要とする場合、リスト内包法を使用できますか?私はこのようなものを探しています(これは機能しません):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
Mappi

2
データフレームにiterrowsを追加すると、行を介して複数の列にアクセスできます:['red' if(row ['Set'] == 'Z')&(row ['Type'] == 'B')else 'green 'インデックスの場合、df.iterrows()の行に挿入]
cheekybastard

1
次のようなデータフレーム内の別のシリーズから置換値を取得する必要がある場合、この素晴らしいソリューションは機能しないことに注意してくださいdf['color_type'] = np.where(df['Set']=='Z', 'green', df['Type'])
Paul Rougieux

@cheekybastardまたはそうしないでください。これ.iterrows()は悪名高いので遅く、DataFrameは反復中に変更しないでください。
AMC

21

これを達成できる別の方法は

df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

良いアプローチです。これは、より大きな効率(より大きなデータセット)のためにメモすることができますが、追加の手順が必要になります。
Yaakov Bressler、

21

ディクショナリーを使用してリスト内のキーに新しい値をマップするこの猫のスキンを作成する別の方法を次に示します。

def map_values(row, values_dict):
    return values_dict[row]

values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}

df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})

df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))

それはどのようなものですか:

df
Out[2]: 
  INDICATOR  VALUE  NEW_VALUE
0         A     10          1
1         B      9          2
2         C      8          3
3         D      7          4

この方法はifelse、作成する-typeステートメントが多い場合(つまり、置換する一意の値が多い場合)に非常に強力です。

そしてもちろん、あなたはいつでもこれを行うことができます:

df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)

しかし、apply私のアプローチでは、そのアプローチは上からのアプローチの3倍以上遅くなります。

そして、あなたはこれを使って、次のことをすることもできますdict.get

df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]

値の複数の置換を行う方法が示されているため、この回答が好きです
Monica Heddneck '21

しかし、そのアプローチは、私のマシンでは、上からの適用アプローチの3倍以上遅くなります。これらをどのようにベンチマークしましたか?私の簡単な測定から、.map()解は〜の10倍高速です.apply()
AMC

更新:100,000,000行、52個の文字列値の場合、.apply()47秒かかりますが、では5.91秒しかかかりません.map()
AMC

19

以下は、ここで示した方法よりも時間がかかりますが、複数の列の内容に基づいて追加の列を計算でき、追加の列に対して3つ以上の値を計算できます。

「Set」列だけを使用した簡単な例:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

より多くの色とより多くの列を考慮した例:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    elif row["Type"] == "C":
        return "blue"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C   blue

編集(21/06/2019):plydataの使用

plydataを使用してこの種のことを行うこともできます(ただし、assignand を使用するよりもさらに遅いようapplyです)。

from plydata import define, if_else

シンプルif_else

df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

ネストif_else

df = define(df, color=if_else(
    'Set=="Z"',
    '"red"',
    if_else('Type=="C"', '"green"', '"blue"')))

print(df)                            
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B   blue
3   Y    C  green

10

多分これはパンダの新しいアップデートで可能だったかもしれませんが、私は以下がこれまでの質問に対する最も短いそしておそらく最良の答えだと思います。.locメソッドを使用して、必要に応じて1つまたは複数の条件を使用できます。

コードの要約:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"

#practice!
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

説明:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))

# df so far: 
  Type Set  
0    A   Z 
1    B   Z 
2    B   X 
3    C   Y

「色」列を追加し、すべての値を「赤」に設定します

df['Color'] = "red"

単一の条件を適用します。

df.loc[(df['Set']=="Z"), 'Color'] = "green"


# df: 
  Type Set  Color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

または必要に応じて複数の条件:

df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Pandasの論理演算子と条件付き選択については、こちらをご覧ください: Pandasでのブールインデックスの論理演算子


2
これまでで最高のもの。おそらく、コードになる条件をさらに追加することができますdf.loc[(df['Set']=="Z") & (df['Type']=="A"), 'Color'] = "green"
Salvador Vigo

2
これは受け入れられる答えになるはずです。実際には慣用的で拡張可能です。
AMC、

1

.apply()メソッドを持つ1つのライナーは次のとおりです。

df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')

その後、dfデータフレームは次のようになります。

>>> print(df)
  Type Set  color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

0

大量のデータを扱う場合は、メモしたアプローチが最適です。

# First create a dictionary of manually stored values
color_dict = {'Z':'red'}

# Second, build a dictionary of "other" values
color_dict_other = {x:'green' for x in df['Set'].unique() if x not in color_dict.keys()}

# Next, merge the two
color_dict.update(color_dict_other)

# Finally, map it to your column
df['color'] = df['Set'].map(color_dict)

この方法は、繰り返し値が多い場合に最も速くなります。私の一般的な経験則は、次の場合にメモすることです:data_size> 10**4n_distinct<data_size/4

例2,500行以下の異なる値を持つ10,000行のメモ化。


さてさて、マッピングする2つの異なる値、100,000,000行だけで、「メモ化」なしで実行するには6.67秒かかり、そうすると9.86秒かかります。
AMC

100,000,000行、52の異なる値。これらの1つは最初の出力値にマップし、他の51はすべて他に対応します。メモ化なしで7.99秒、メモありで11.1秒。
AMC

値は順不同ですか?それとも背中合わせですか?パンダの高速化は@AMCのキャッシュが原因である可能性があります
Yaakov

1
値は順不同ですか?それとも背中合わせですか?値はランダムで、を使用して選択されrandom.choices()ます。
AMC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.