パンダは他の列の値に基づいて新しい列を作成します/行ごとに複数の列の関数を適用します


316

私は、これらの6つの列(に(それがあれば、他のはしごを使用しています)私のカスタム関数を適用したいERI_HispanicERI_AmerInd_AKNatvERI_AsianERI_Black_Afr.AmerERI_HI_PacIslERI_White私のデータフレームの各行の)。

私は他の質問とは異なる方法を試しましたが、それでも私の問題に対する正しい答えを見つけることができないようです。これの重要な部分は、人がヒスパニックとして数えられるならば、彼らは他のものとして数えることができないということです。別の民族の列に「1」がある場合でも、2人以上ではなくヒスパニック系としてカウントされます。同様に、すべてのERI列の合計が1より大きい場合、それらは2つ以上の人種としてカウントされ、一意の民族として数えることはできません(ヒスパニックを除く)。うまくいけば、これは理にかなっています。どんな助けでも大歓迎です。

これは、各行でforループを実行するのとほとんど同じです。各レコードが基準を満たしている場合は、1つのリストに追加され、元のリストから削除されます。

以下のデータフレームから、SQLの次の仕様に基づいて新しい列を計算する必要があります。

=========================基準======================== =======

IF [ERI_Hispanic] = 1 THEN RETURN Hispanic
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN Two or More
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN A/I AK Native
ELSE IF [ERI_Asian] = 1 THEN RETURN Asian
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN Black/AA
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN White

コメント:ヒスパニックのERIフラグがTrue(1)の場合、従業員は「ヒスパニック」として分類されます。

コメント:ヒスパニックでないERIフラグが複数ある場合は、「2つ以上」を返す

======================データフレーム===========================

     lname          fname       rno_cd  eri_afr_amer    eri_asian   eri_hawaiian    eri_hispanic    eri_nat_amer    eri_white   rno_defined
0    MOST           JEFF        E       0               0           0               0               0               1           White
1    CRUISE         TOM         E       0               0           0               1               0               0           White
2    DEPP           JOHNNY              0               0           0               0               0               1           Unknown
3    DICAP          LEO                 0               0           0               0               0               1           Unknown
4    BRANDO         MARLON      E       0               0           0               0               0               0           White
5    HANKS          TOM         0                       0           0               0               0               1           Unknown
6    DENIRO         ROBERT      E       0               1           0               0               0               1           White
7    PACINO         AL          E       0               0           0               0               0               1           White
8    WILLIAMS       ROBIN       E       0               0           1               0               0               0           White
9    EASTWOOD       CLINT       E       0               0           0               0               0               1           White

特定の関数は、いくつかの変数の値が他の変数よりも優先される、長いif-elseラダーです。ハードウェアエンジニアリングの用語では、これはプライオリティデコーダと呼ばれます。
smci

回答:


408

さて、これへの2つのステップ-最初に、あなたが望む翻訳を行う関数を書くことです-私はあなたの疑似コードに基づいて例をまとめました:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

あなたはこれを調べたいかもしれませんが、それはトリックを行うようです-関数に入るパラメータは「行」というラベルの付いたシリーズオブジェクトであると考えられることに注意してください。

次に、pandasのapply関数を使用して関数を適用します-例

df.apply (lambda row: label_race(row), axis=1)

axis = 1指定子に注意してください。これは、アプリケーションが列レベルではなく行で実行されることを意味します。結果は次のとおりです。

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White

これらの結果に満足したら、もう一度実行して、結果を元のデータフレームの新しい列に保存します。

df['race_label'] = df.apply (lambda row: label_race(row), axis=1)

結果のデータフレームは次のようになります(新しい列を表示するには右にスクロールします)。

      lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label
0      MOST    JEFF      E             0          0             0              0             0          1       White         White
1    CRUISE     TOM      E             0          0             0              1             0          0       White      Hispanic
2      DEPP  JOHNNY    NaN             0          0             0              0             0          1     Unknown         White
3     DICAP     LEO    NaN             0          0             0              0             0          1     Unknown         White
4    BRANDO  MARLON      E             0          0             0              0             0          0       White         Other
5     HANKS     TOM    NaN             0          0             0              0             0          1     Unknown         White
6    DENIRO  ROBERT      E             0          1             0              0             0          1       White   Two Or More
7    PACINO      AL      E             0          0             0              0             0          1       White         White
8  WILLIAMS   ROBIN      E             0          0             1              0             0          0       White  Haw/Pac Isl.
9  EASTWOOD   CLINT      E             0          0             0              0             0          1       White         White

69
ちょうどノート:あなただけの関数に行を供給している場合、あなただけ行うことができます:df.apply(label_race, axis=1)
ポール・H

1
別の行で同様のことをしたい場合は、同じ関数を使用できますか?たとえば、結果から、['race_label'] == "White"の場合、 'White'が返されます。ただし、['race_label'] == 'Unknown'の場合、['rno_defined']列から値が返されます。同じ関数が機能すると思いますが、他の列から値を取得する方法がわかりません。
Dave

2
あなたは「race_label」フィールドで、そのルックスを新しい関数を記述し、新しいフィールドに結果を送信、または可能性-と私は最終的に変更、これはこの場合、編集元の関数では、より良いかもしれないと思うreturn 'Other'ラインをへreturn row['rno_defined']ているべきif / thenステートメントのセットで一致が見つからない場合(つまり、現在、「その他」が表示されている場合)は、その列の値を代入します。
Thomas Kimber

9
簡略化できます:df.apply(lambda row: label_race (row),axis=1)todf.apply(label_race, axis=1)
user48956 2017年

5
新しいバージョンでは、「SettingWithCopyWarning」を取得した場合、「assign」メソッドを確認する必要があります。参照:stackoverflow.com/a/12555510/3015186
np8 2017年

218

これは「他のパンダの新しい列」の最初のGoogle結果なので、簡単な例を次に示します。

import pandas as pd

# make a simple dataframe
df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
df
#    a  b
# 0  1  3
# 1  2  4

# create an unattached column with an index
df.apply(lambda row: row.a + row.b, axis=1)
# 0    4
# 1    6

# do same but attach it to the dataframe
df['c'] = df.apply(lambda row: row.a + row.b, axis=1)
df
#    a  b  c
# 0  1  3  4
# 1  2  4  6

あなたがそれを手に入れれば、SettingWithCopyWarningあなたはこの方法でもそれを行うことができます:

fn = lambda row: row.a + row.b # define a function for the new column
col = df.apply(fn, axis=1) # get column data with an index
df = df.assign(c=col.values) # assign values to column 'c'

ソース:https : //stackoverflow.com/a/12555510/243392

列名にスペースが含まれている場合は、次のような構文を使用できます。

df = df.assign(**{'some column name': col.values})

そして、ここにapplyassignのドキュメントがあります


1
短い答え、本質にまで蒸留されました!
Frode Akselsen 2017年

1
私がやっているSettingWithCopyWarningときに私df['c'] = df.apply(lambda row: row.a + row.b, axis=1) はそれを得ていますそれはここで本当の問題ですか、それとも心配する必要はありませんか?
ネイト

2
@Nateその警告を受けたことはありません-多分それはデータフレームのデータに依存しますか?しかし、私は2017年から、別の回答に基づいて回答ammended
ブライアン・バーンズを

57

上記の回答は完全に有効ですが、の形式でベクトル化されたソリューションが存在しnumpy.selectます。これにより、条件を定義し、それらの条件の出力を定義できます。使用するよりもはるかに効率的ですapply


まず、条件を定義します。

conditions = [
    df['eri_hispanic'] == 1,
    df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    df['eri_nat_amer'] == 1,
    df['eri_asian'] == 1,
    df['eri_afr_amer'] == 1,
    df['eri_hawaiian'] == 1,
    df['eri_white'] == 1,
]

次に、対応する出力を定義します。

outputs = [
    'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]

最後に、使用numpy.select

res = np.select(conditions, outputs, 'Other')
pd.Series(res)

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White
dtype: object

なぜnumpy.select使用する必要がありapplyますか?ここにいくつかのパフォーマンスチェックがあります:

df = pd.concat([df]*1000)

In [42]: %timeit df.apply(lambda row: label_race(row), axis=1)
1.07 s ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [44]: %%timeit
    ...: conditions = [
    ...:     df['eri_hispanic'] == 1,
    ...:     df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    ...:     df['eri_nat_amer'] == 1,
    ...:     df['eri_asian'] == 1,
    ...:     df['eri_afr_amer'] == 1,
    ...:     df['eri_hawaiian'] == 1,
    ...:     df['eri_white'] == 1,
    ...: ]
    ...:
    ...: outputs = [
    ...:     'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
    ...: ]
    ...:
    ...: np.select(conditions, outputs, 'Other')
    ...:
    ...:
3.09 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

使い方は、numpy.select私たちに与え大幅に改善された性能を、データが大きくなるにつれてずれが唯一増加します。


8
このソリューションは過小評価されています。applyを使用して同様のことができることは知っていましたが、何千ものファイルに対してその操作を実行する必要があるため、別の方法を探していました。私はあなたの投稿を見つけてうれしいです。
mlx

私は同様のものを作成するのに苦労しています。「シリーズの真理値があいまいです...」というエラーメッセージが表示されます。私のコードはKansas_City = ['ND'、 'SD'、 'NE'、 'KS'、 'MN'、 'IA'、 'MO']条件= [Kansas_City]の[df_merge ['state_alpha']出力= ['カンザスシティ '] df_merge [' Region '] = np.select(conditions、output、' Other ')何か助けはありますか?
Shawn Schreier

3
これは受け入れられる答えになるはずです。他のものは問題ありませんが、より大きなデータで作業すると、これが機能する唯一のものであり、驚くほど速く機能します。
TheProletariat

29

.apply()最初のパラメータとして関数を受け取ります。次のlabel_raceように関数を渡します:

df['race_label'] = df.apply(label_race, axis=1)

関数を渡すためにラムダ関数を作成する必要はありません。


12

これを試して、

df.loc[df['eri_white']==1,'race_label'] = 'White'
df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
df['race_label'].fillna('Other', inplace=True)

O / P:

     lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian  \
0      MOST    JEFF      E             0          0             0   
1    CRUISE     TOM      E             0          0             0   
2      DEPP  JOHNNY    NaN             0          0             0   
3     DICAP     LEO    NaN             0          0             0   
4    BRANDO  MARLON      E             0          0             0   
5     HANKS     TOM    NaN             0          0             0   
6    DENIRO  ROBERT      E             0          1             0   
7    PACINO      AL      E             0          0             0   
8  WILLIAMS   ROBIN      E             0          0             1   
9  EASTWOOD   CLINT      E             0          0             0   

   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label  
0             0             0          1       White         White  
1             1             0          0       White      Hispanic  
2             0             0          1     Unknown         White  
3             0             0          1     Unknown         White  
4             0             0          0       White         Other  
5             0             0          1     Unknown         White  
6             0             0          1       White   Two Or More  
7             0             0          1       White         White  
8             0             0          0       White  Haw/Pac Isl.  
9             0             0          1       White         White 

.loc代わりに使用してくださいapply

それはベクトル化を改善します。

.loc 単純な方法で機能し、条件に基づいて行をマスクし、フリーズ行に値を適用します。

詳細については、.locドキュメントをご覧ください。

パフォーマンスメトリック:

受け入れられた回答:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

df=pd.read_csv('dataser.csv')
df = pd.concat([df]*1000)

%timeit df.apply(lambda row: label_race(row), axis=1)

ループあたり1.15秒±46.5ミリ秒(7回の実行の平均±標準偏差、それぞれ1ループ)

私の提案された答え:

def label_race(df):
    df.loc[df['eri_white']==1,'race_label'] = 'White'
    df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
    df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
    df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
    df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
    df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
    df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
    df['race_label'].fillna('Other', inplace=True)
df=pd.read_csv('s22.csv')
df = pd.concat([df]*1000)

%timeit label_race(df)

ループあたり24.7 ms±1.7 ms(7回の実行の平均±標準偏差、それぞれ10ループ)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.