Панди: заповнення відсутніх значень середнім значенням у кожній групі


88

Це має бути просто, але найближчим, що я знайшов, є цей пост: pandas: Заповнення відсутніх значень у групі , і я все ще не можу вирішити свою проблему ....

Припустимо, у мене є такий фрейм даних

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", тобто

      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()

Спасибі купу.

Відповіді:


94

Одним із способів було б використовувати 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

Також зверніть увагу на книгу Веса Мак-Кінні. Особисто я вважаю, що документи на групування є жахливими, книга трохи краща.
Woody Pride

38
якщо у вас більше двох стовпців, не забудьте вказати ім'я стовпця df ["value"] = df.groupby ("name"). transform (lambda x: x.fillna (x.mean ())) ['value ']
Лорен

16
@Lauren Гарна думка. Я хотів би додати, що з міркувань продуктивності ви можете розглянути можливість переміщення специфікації стовпця значення далі вліво до речення group-by. Таким чином, лямбда-функція викликається лише для значень у цьому конкретному стовпці, а не для кожного стовпця, а потім вибирається стовпець. Зробив тест, і це було вдвічі швидше при використанні двох стовпців. І, природно, ви отримуєте кращу продуктивність, чим більше стовпців вам не потрібно вводити:df["value"] = df.groupby("name")["value"].transform(lambda x: x.fillna(x.mean()))
Андре К. Андерсен

Я шукав це два дні .. Просто питання до вас. Чому це занадто важко зробити з циклами? Тому що в моєму випадку є два мульти індексів , тобто Stateі Age_Groupя намагаюся заповнити відсутні значення в цих групах з групою засобів (з того ж держави в тій же віковій групі приймає середні і заливальні missings в групі) .. Спасибі
Озкан Serttas

51

fillna+ groupby+ transform+mean

Це здається інтуїтивно зрозумілим:

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

groupby+ transformСинтаксис відображає GROUPWISE середнє значення для індексу вихідного dataframe. Це приблизно еквівалентно рішенню @ 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

У цьому узагальненому випадку ми хотіли б згрупувати за categoryі name, і вказувати лише на value.

Це можна вирішити наступним чином:

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

Зверніть увагу на список стовпців у реченні про групу та про те, що ми вибираємо valueстовпець відразу після групи. Це робить перетворення запущеним лише у цьому конкретному стовпці. Ви можете додати його до кінця, але тоді ви будете запускати його для всіх стовпців, лише щоб викинути всі, крім одного стовпчика міри в кінці. Стандартний планувальник запитів SQL міг би це змогти оптимізувати, але pandas (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
Ozkan 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

10

Більшість наведених вище відповідей стосуються використання "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

Рекомендована високопоставлена ​​відповідь працює лише для pandas Dataframe лише з двома стовпцями. Якщо у вас більше випадків стовпців, використовуйте замість цього:

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

Ця відповідь спрацювала для мене, дякую. Також для тих, хто новачок у пандах, може також індексувати, використовуючи позначення нарізки. df.groupby("continent")['Crude_Birth_rate']... Я вважаю, що це пропонована ковненція
Адам Хьюз,


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

5
Будь ласка, дайте пояснення своєї відповіді. Чому хтось, хто натрапляє на цю сторінку від Google, повинен використовувати ваше рішення над іншими 6 відповідями?
divibisan

1
@vino, будь ласка, додайте пояснення
Нурснааз,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.