パンダの別の値に基づいて1つの値を変更する


107

私はStataコードをPythonに再プログラムして速度を改善しようとしていますが、PANDASの方向を指摘されました。しかし、データの処理方法に頭を悩ませています。

列見出し「ID」のすべての値を反復処理するとします。そのIDが特定の番号と一致する場合、対応する2つの値FirstNameとLastNameを変更します。

Stataでは、次のようになります。

replace FirstName = "Matt" if ID==103
replace LastName =  "Jones" if ID==103

したがって、これはID == 103の値に対応するFirstNameのすべての値をMattに置き換えます。

PANDASで、私はこのようなものを試しています

df = read_csv("test.csv")
for i in df['ID']:
    if i ==103:
          ...

ここからどこに行くべきかわからない。何か案は?

回答:


180

1つのオプションは、Pythonのスライス機能とインデックス機能を使用して、条件が満たされている場所を論理的に評価し、そこでデータを上書きすることです。

あなたが直接にデータをロードすることができますと仮定pandasしてpandas.read_csv、次のコードは、あなたのために役に立つかもしれません。

import pandas
df = pandas.read_csv("test.csv")
df.loc[df.ID == 103, 'FirstName'] = "Matt"
df.loc[df.ID == 103, 'LastName'] = "Jones"

コメントで述べたように、両方の列への割り当てを一度に行うこともできます。

df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'

割り当ての上書き操作にpandas使用するにはloc、バージョン0.11以降が必要です。


もう1つの方法は、いわゆる連鎖割り当てを使用することです。この動作は安定性が低いため、最適なソリューションとは見なされません(ドキュメントでは明示的に推奨されていません)が、次のことを知っておくと役立ちます。

import pandas
df = pandas.read_csv("test.csv")
df['FirstName'][df.ID == 103] = "Matt"
df['LastName'][df.ID == 103] = "Jones"

16
このフレーバーも追加するのはどうdf.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'
ですか

2
-1 "これを行う別の方法は、いわゆる連鎖割り当てを使用することです。" いいえ、強調します。それはだだけチェーンの割り当てが信頼できないことを知っておくと便利。それが信頼できる、最適ではないソリューションであるということではありません、状況ははるかに悪いです。Stack Overflowの他の場所でもこれを認めています。連鎖割り当てが実行可能なオプションであるという幻想を与えないようにしてください。あなたが与えた最初の2つの方法は十分であり、これを行うための好ましい方法です。
Phillip Cloud

9
同意しません。なぜ連鎖代入が実行可能な方法ではないと断定的に主張しようとしているのか理解できません。私はそれが好ましい方法とは見なされないことを認めました。これ以上何が欲しいですか。これがないように行動することが不合理だ、それを行う方法。実際、現在のシステム(バージョン0.8)では、これを行うのが正しい方法です。あなたがこの立場を取るつもりなら、私はあなたの賛成票には興味がありません。反対投票であなたのポイントを知らせてください。しかし、私はすでにあなたのポイントを反映しており、同意していません。
2013年

11
インターネットは深刻なビジネスです。とにかく、EMS、オプションが存在することを知って感謝しています。
Parseltongue 2013年

あなたが遭遇するかもしれない1つの問題は、csvに列名にピリオド/ドットがあり、割り当てがめちゃくちゃになることです。次のようなものを使用して列を修正できます:cols = df.columns cols = cols.map(lambda x:x.replace( '。'、 '_')if isinstance(x、str)else x)df.columns = cols
ski_squaw

37

を使用できますmap。辞書やカスタム関数からの値をマッピングできます。

これがあなたのdfだとしましょう:

    ID First_Name Last_Name
0  103          a         b
1  104          c         d

辞書を作成します。

fnames = {103: "Matt", 104: "Mr"}
lnames = {103: "Jones", 104: "X"}

そしてマップ:

df['First_Name'] = df['ID'].map(fnames)
df['Last_Name'] = df['ID'].map(lnames)

結果は次のようになります。

    ID First_Name Last_Name
0  103       Matt     Jones
1  104         Mr         X

または、カスタム関数を使用します。

names = {103: ("Matt", "Jones"), 104: ("Mr", "X")}
df['First_Name'] = df['ID'].map(lambda x: names[x][0])

2
辞書に値が存在しない場合、KeyErrorが生成されませんか?
EdChum 2013年

1
カスタム関数は、他のものはとにかく動作します。しかし、私dictはマッピング用に作成されたと想定しました。それ以外の場合は、いくつかのチェック/清掃はのようなものに基づいて行うことができますdf.ID.isin(names.keys())
ルトガーKassies

カスタム関数は、任意の(非匿名)関数に展開できます。
user989762

14

元の質問は、特定の狭い使用例を扱っています。より一般的な答えが必要な人のためにここにいくつかの例があります:

他の列のデータを使用して新しい列を作成する

以下のデータフレームがあるとします:

import pandas as pd
import numpy as np

df = pd.DataFrame([['dog', 'hound', 5],
                   ['cat', 'ragdoll', 1]],
                  columns=['animal', 'type', 'age'])

In[1]:
Out[1]:
  animal     type  age
----------------------
0    dog    hound    5
1    cat  ragdoll    1

以下では、シリーズに対してオーバーライドされる操作をdescription使用して、新しい列を他の列の連結として追加しています+。は文字列に+適用され、「プリミティブ」値には適用されないため、派手な文字列フォーマット、f文字列などはここでは機能しません。

df['description'] = 'A ' + df.age.astype(str) + ' years old ' \
                    + df.type + ' ' + df.animal

In [2]: df
Out[2]:
  animal     type  age                description
-------------------------------------------------
0    dog    hound    5    A 5 years old hound dog
1    cat  ragdoll    1  A 1 years old ragdoll cat

1 years(の代わりに1 year)猫を取得します。これは、条件文を使用して以下で修正します。

条件付きで既存の列を変更する

ここでは、元のanimal列を他の列の値で置き換え、np.whereの値に基づいて条件付き部分文字列を設定するために使用していますage

# append 's' to 'age' if it's greater than 1
df.animal = df.animal + ", " + df.type + ", " + \
    df.age.astype(str) + " year" + np.where(df.age > 1, 's', '')

In [3]: df
Out[3]:
                 animal     type  age
-------------------------------------
0   dog, hound, 5 years    hound    5
1  cat, ragdoll, 1 year  ragdoll    1

条件付きで複数の列を変更する

より柔軟なアプローチは.apply()、単一の列ではなくデータフレーム全体を呼び出すことです。

def transform_row(r):
    r.animal = 'wild ' + r.type
    r.type = r.animal + ' creature'
    r.age = "{} year{}".format(r.age, r.age > 1 and 's' or '')
    return r

df.apply(transform_row, axis=1)

In[4]:
Out[4]:
         animal            type      age
----------------------------------------
0    wild hound    dog creature  5 years
1  wild ragdoll    cat creature   1 year

上記のコードでは、transform_row(r)関数はSeries指定された行を表すオブジェクトを受け取ります(で示されaxis=1、のデフォルト値は各列にオブジェクトをaxis=0提供しSeriesます)。これにより、列名を使用して行の実際の「プリミティブ」値にアクセスし、特定の行/列の他のセルを表示できるため、処理が簡略化されます。


1
そのような包括的な答えを書くために時間を割いてくれてありがとう。とても有難い。
パーセルタング

この非常に役立つ回答に感謝します。1つのフォローアップ-文字列を変更するのではなく、列で数学を実行して列を変更したい場合はどうなりますか?たとえば、上記の例を使用して、df.animal == 'dog'の場合にdf.age列に7を掛けるとどうなりますか?ありがとうございました!
GbG

1
@GbG:np.whereおそらくあなたが探しているものです、たとえば、stackoverflow.com / a / 42540310/191246を 参照してください。ただし、ロジックをスカラー演算に適合させることができず、明示的に変換する必要がある場合もあります。数値的にはセルと同じようにセルtransform_row
ccpizza

@ccpizzaありがとうございます!まさに私が探していたもの。
GbG

13

この質問は、Kassies氏の回答に補遺を提供する価値があるほど頻繁にアクセスされる可能性があります。dictデフォルトは「行方不明」のキーに対して返されるように内蔵されたクラスは、サブ分類することができます。このメカニズムはパンダに適しています。ただし、以下を参照してください。

このようにして、主要なエラーを回避することができます。

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> class SurnameMap(dict):
...     def __missing__(self, key):
...         return ''
...     
>>> surnamemap = SurnameMap()
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap[x])
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

同じことは、次の方法でより簡単に行うことができます。getdictオブジェクトのメソッドに'default'引数を使用すると、dictをサブクラス化する必要がなくなります。

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> surnamemap = {}
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap.get(x, ''))
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

1
これは、私が見た中で最も優れた最も簡単な答えであり、デフォルトの処理が優れています。ありがとうございました。
ブレンダン

@ブレンダン:ああ!どうもありがとう。
ビル・ベル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.