パンダ:各グループの平均で不足している値を埋める


83

これは簡単なはずですが、私が見つけた最も近いものはこの投稿です: パンダ:グループ内の不足している値を埋めていますが、それでも問題を解決できません...。

次のデータフレームがあるとします

df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3], 'name': ['A','A', 'B','B','B','B', 'C','C','C']})

  name  value
0    A      1
1    A    NaN
2    B    NaN
3    B      2
4    B      3
5    B      1
6    C      3
7    C    NaN
8    C      3

そして、「NaN」に各「名前」グループの平均値を入力したいと思います。

      name  value
0    A      1
1    A      1
2    B      2
3    B      2
4    B      3
5    B      1
6    C      3
7    C      3
8    C      3

どこに行けばいいのかわからない:

grouped = df.groupby('name').mean()

本当にありがとう。

回答:


91

1つの方法は、次を使用することtransformです。

>>> df
  name  value
0    A      1
1    A    NaN
2    B    NaN
3    B      2
4    B      3
5    B      1
6    C      3
7    C    NaN
8    C      3
>>> df["value"] = df.groupby("name").transform(lambda x: x.fillna(x.mean()))
>>> df
  name  value
0    A      1
1    A      1
2    B      2
3    B      2
4    B      3
5    B      1
6    C      3
7    C      3
8    C      3

3
座ってドキュメントを読み始めるときに役立つことがわかりました。これはgroupbyセクションでカバーされています。覚えておくべきことが多すぎますが、「変換は、元のフレームのようにインデックスを作成するグループごとの操作用です」などのルールを選択します。
DSM

ウェス・マッキンニーの本も探してください。個人的には、groupbyのドキュメントはひどいものだと思いますが、この本の方がわずかに優れています。
ウッディプライド

35
3つ以上の列がある場合は、列名を必ず指定してくださいdf ["value"] = df.groupby( "name")。transform(lambda x:x.fillna(x.mean()))['value ']
ローレン

16
@ローレン良い点。パフォーマンス上の理由から、値列の指定をさらに左のgroup-by句に移動することを検討してください。このように、ラムダ関数はその特定の列の値に対してのみ呼び出され、すべての列ではなく、列を選択します。テストを行ったところ、2つのカラムを使用した場合の速度は2倍でした。:そして当然、あなたが転嫁する必要はありません複数の列より良いパフォーマンスを得るdf["value"] = df.groupby("name")["value"].transform(lambda x: x.fillna(x.mean()))
アンドレ・C.アンデルセン

私はこれを2日間探していました。あなたへの質問です。ループでこれを行うのが難しいのはなぜですか?私の場合には2つのマルチインデックスは、IEがあるのでStateAge_Group(同じ状態から同じ年齢グループ内のグループの平均フィルmissingsを取る)、私はグループ手段と、これらのグループで欠損値を埋めるためにしようとしています。..おかげ
オズカンSerttas

45

fillna+ groupby+ transform+mean

これは直感的に思えます:

df['value'] = df['value'].fillna(df.groupby('name')['value'].transform('mean'))

groupby+のtransform構文は、元のデータフレームのインデックスにGroupWiseで平均値をマッピングします。これは@DSMのソリューションとほぼ同等ですが、無名lambda関数を定義する必要がありません。


25

@DSMにはIMOの正解がありますが、質問の一般化と最適化を共有したいと思います。複数の列をグループ化して、複数の値の列を作成します。

df = pd.DataFrame(
    {
        'category': ['X', 'X', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y'],
        'name': ['A','A', 'B','B','B','B', 'C','C','C'],
        'other_value': [10, np.nan, np.nan, 20, 30, 10, 30, np.nan, 30],
        'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3],
    }
)

...与える...

  category name  other_value value
0        X    A         10.0   1.0
1        X    A          NaN   NaN
2        X    B          NaN   NaN
3        X    B         20.0   2.0
4        X    B         30.0   3.0
5        X    B         10.0   1.0
6        Y    C         30.0   3.0
7        Y    C          NaN   NaN
8        Y    C         30.0   3.0

この一般化されたケースでは、とでグループ化しcategorynameにのみ代入しvalueます。

これは次のように解決できます。

df['value'] = df.groupby(['category', 'name'])['value']\
    .transform(lambda x: x.fillna(x.mean()))

group-by句の列リストと、group-byのvalue直後の列を選択していることに注意してください。これにより、変換はその特定の列でのみ実行されます。これを最後に追加することもできますが、すべての列に対して実行して、最後に1つのメジャー列を除くすべてを破棄します。標準のSQLクエリプランナーはこれを最適化できたかもしれませんが、パンダ(0.19.2)はこれを行っていないようです。

実行してデータセットを増やすことによるパフォーマンステスト...

big_df = None
for _ in range(10000):
    if big_df is None:
        big_df = df.copy()
    else:
        big_df = pd.concat([big_df, df])
df = big_df

...これにより、代入する必要のない列の数に比例して速度が上がることを確認します。

import pandas as pd
from datetime import datetime

def generate_data():
    ...

t = datetime.now()
df = generate_data()
df['value'] = df.groupby(['category', 'name'])['value']\
    .transform(lambda x: x.fillna(x.mean()))
print(datetime.now()-t)

# 0:00:00.016012

t = datetime.now()
df = generate_data()
df["value"] = df.groupby(['category', 'name'])\
    .transform(lambda x: x.fillna(x.mean()))['value']
print(datetime.now()-t)

# 0:00:00.030022

最後に、すべてではありませんが、複数の列を代入する場合は、さらに一般化できます。

df[['value', 'other_value']] = df.groupby(['category', 'name'])['value', 'other_value']\
    .transform(lambda x: x.fillna(x.mean()))

この素晴らしい仕事をありがとう。forループを使用して同じ変換を成功させるにはどうすればよいのでしょうか。私は手動の方法を見つけようとしているので、速度は私の関心事ではありません。AndréC.Andersen@感謝
オズカンSerttas

12

私はこのようにします

df.loc[df.value.isnull(), 'value'] = df.groupby('group').value.transform('mean')

1
これにわずかに異なるバージョンdf['value_imputed'] = np.where(df.value.isnull(), df.groupby('group').value.transform('mean'), df.value)
tsando

9

上記の回答のほとんどは、「groupby」と「transform」を使用して不足している値を埋めることに関係していました。

しかし、私は「groupby」と「apply」を使用して不足している値を埋めることを好みます。これは私にとってより直感的です。

>>> df['value']=df.groupby('name')['value'].apply(lambda x:x.fillna(x.mean()))
>>> df.isnull().sum().sum()
    0 

ショートカット:Groupby + Apply / Lambda + Fillna + Mean

このソリューションは、欠落している値を置き換えるために複数の列でグループ化する場合でも機能します。

     >>> df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, np.nan,np.nan, 4, 3], 
    'name': ['A','A', 'B','B','B','B', 'C','C','C'],'class':list('ppqqrrsss')})  

     >>> df
   value name   class
0    1.0    A     p
1    NaN    A     p
2    NaN    B     q
3    2.0    B     q
4    3.0    B     r
5    NaN    B     r
6    NaN    C     s
7    4.0    C     s
8    3.0    C     s

>>> df['value']=df.groupby(['name','class'])['value'].apply(lambda x:x.fillna(x.mean()))

>>> df
        value name   class
    0    1.0    A     p
    1    1.0    A     p
    2    2.0    B     q
    3    2.0    B     q
    4    3.0    B     r
    5    3.0    B     r
    6    3.5    C     s
    7    4.0    C     s
    8    3.0    C     s

5

注目の上位回答は、2列しかないパンダデータフレームでのみ機能します。より多くの列がある場合は、代わりに次を使用してください。

df['Crude_Birth_rate'] = df.groupby("continent").Crude_Birth_rate.transform(
    lambda x: x.fillna(x.mean()))

この答えは私のために働いた、ありがとう。また、パンダに新しい人のために、また、スライシングを使用してインデックスすることができます記法df.groupby("continent")['Crude_Birth_rate']... 私は、これは推奨さcovnentionであると信じて
アダム・ヒューズ

2
def groupMeanValue(group):
    group['value'] = group['value'].fillna(group['value'].mean())
    return group

dft = df.groupby("name").transform(groupMeanValue)

0
df.fillna(df.groupby(['name'], as_index=False).mean(), inplace=True)

5
あなたの答えを説明してください。グーグルからこのページに出くわした誰かが他の6つの答えよりもあなたの解決策を使うべきなのはなぜですか?
divibisan 2018年

1
@vinoいくつかの説明を追加してください
Nursnaaz

-1

を使用することもできます"dataframe or table_name".apply(lambda x: x.fillna(x.mean()))

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