Gấu trúc: điền các giá trị còn thiếu theo trung bình trong mỗi nhóm


83

Điều này nên đơn giản, nhưng điều gần nhất mà tôi tìm thấy là bài đăng này: pandas: Điền các giá trị bị thiếu trong một nhóm và tôi vẫn không thể giải quyết vấn đề của mình ....

Giả sử tôi có khung dữ liệu sau

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

và tôi muốn điền "NaN" với giá trị trung bình trong mỗi nhóm "tên", tức là

      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

Tôi không chắc sẽ đi đâu sau:

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

Cảm ơn nhiều.

Câu trả lời:


91

Một cách sẽ là sử dụng 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
Tôi thấy hữu ích khi bắt đầu ngồi xuống và đọc qua các tài liệu. Điều này được đề cập trong groupbyphần. Có quá nhiều thứ để nhớ, nhưng bạn chọn các quy tắc như "chuyển đổi dành cho các hoạt động của mỗi nhóm mà bạn muốn lập chỉ mục giống như khung ban đầu", v.v.
DSM

Ngoài ra, hãy tìm cuốn sách của Wes McKinney. Cá nhân tôi nghĩ rằng các tài liệu về groupby là không ổn, cuốn sách tốt hơn một chút.
Woody Pride

35
nếu bạn có nhiều hơn hai cột, hãy đảm bảo chỉ định tên cột df ["value"] = df.groupby ("name"). biến đổi (lambda x: x.fillna (x.mean ())) ['value ']
Lauren

16
@Lauren Điểm tốt. Tôi muốn thêm điều đó vì lý do hiệu suất, bạn có thể cân nhắc để di chuyển đặc tả cột giá trị sang trái thêm mệnh đề từng nhóm. Theo cách này, hàm lambda chỉ được gọi cho các giá trị trong cột cụ thể đó, chứ không phải mọi cột rồi chọn cột. Đã làm một thử nghiệm và nó nhanh gấp đôi khi sử dụng hai cột. Và đương nhiên bạn sẽ có được hiệu suất tốt hơn nhiều cột mà bạn không cần phải quy cho:df["value"] = df.groupby("name")["value"].transform(lambda x: x.fillna(x.mean()))
André C. Andersen

Tôi đã tìm kiếm điều này trong hai ngày .. Chỉ là một câu hỏi cho bạn. Tại sao quá khó để làm điều này với các vòng lặp? Bởi vì trong trường hợp của tôi có hai chỉ mục đa nghĩa StateAge_Groupsau đó tôi đang cố gắng lấp đầy các giá trị còn thiếu trong các nhóm đó bằng giá trị nhóm (từ cùng một tiểu bang trong cùng một nhóm tuổi lấy giá trị trung bình và điền vào các chỗ thiếu trong nhóm) .. Cảm ơn
Ozkan Serttas

45

fillna+ groupby+ transform+mean

Điều này có vẻ trực quan:

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

Các groupby+ transformcú pháp bản đồ giá trị trung bình GroupWise sang các chỉ số của dataframe gốc. Điều này gần tương đương với giải pháp của @ DSM , nhưng tránh được sự cần thiết phải xác định một lambdahàm ẩn danh .


25

@DSM có IMO câu trả lời đúng, nhưng tôi muốn chia sẻ sự khái quát và tối ưu hóa của tôi cho câu hỏi: Nhiều cột thành từng nhóm và có nhiều cột giá trị:

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],
    }
)

... cho ...

  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

Trong trường hợp tổng quát này, chúng tôi muốn nhóm theo categorynamevà chỉ hàm ý trên value.

Điều này có thể được giải quyết như sau:

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

Lưu ý danh sách cột trong mệnh đề từng nhóm và chúng tôi chọn valuecột ngay sau từng nhóm. Điều này làm cho phép biến đổi chỉ được chạy trên cột cụ thể đó. Bạn có thể thêm nó vào cuối, nhưng sau đó bạn sẽ chạy nó cho tất cả các cột chỉ để loại bỏ tất cả trừ một cột đo lường ở cuối. Một công cụ lập kế hoạch truy vấn SQL chuẩn có thể đã tối ưu hóa điều này, nhưng gấu trúc (0.19.2) dường như không làm được điều này.

Kiểm tra hiệu suất bằng cách tăng tập dữ liệu bằng cách thực hiện ...

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

... xác nhận rằng điều này làm tăng tốc độ tương ứng với số cột bạn không phải áp dụng:

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

Lưu ý cuối cùng, bạn có thể tổng quát hóa hơn nữa nếu bạn muốn áp dụng nhiều hơn một cột, nhưng không phải tất cả:

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

Cảm ơn bạn vì công việc tuyệt vời này. Tôi đang tự hỏi làm thế nào tôi có thể thành công cùng một chuyển đổi bằng cách sử dụng forcác vòng lặp. Tốc độ không phải là mối quan tâm của tôi vì tôi đang cố gắng tìm các phương pháp thủ công. Cảm ơn @ AndréC.Andersen
Ozkan Serttas,

12

Tôi sẽ làm theo cách này

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

1
Một phiên bản hơi khác nhau nàydf['value_imputed'] = np.where(df.value.isnull(), df.groupby('group').value.transform('mean'), df.value)
tsando

9

Hầu hết các câu trả lời ở trên liên quan đến việc sử dụng "groupby" và "biến đổi" để điền vào các giá trị còn thiếu.

Nhưng tôi thích sử dụng "groupby" với "apply" để điền vào các giá trị còn thiếu, trực quan hơn đối với tôi.

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

Phím tắt: Groupby + Apply / Lambda + Fillna + Mean

Giải pháp này vẫn hoạt động nếu bạn muốn nhóm theo nhiều cột để thay thế các giá trị bị thiếu.

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

Câu trả lời được xếp hạng cao nổi bật chỉ hoạt động với Khung dữ liệu gấu trúc chỉ có hai cột. Nếu bạn có nhiều cột hơn, hãy sử dụng thay thế:

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

Câu trả lời này đã làm việc cho tôi, cảm ơn. Ngoài ra cho bất cứ ai mới để gấu trúc, cũng có thể chỉ sử dụng ký hiệu cắt df.groupby("continent")['Crude_Birth_rate']... Tôi tin rằng đây là gợi ý covnention
Adam Hughes

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
Vui lòng đưa ra một số giải thích về câu trả lời của bạn. Tại sao một người tình cờ tìm thấy trang này từ google nên sử dụng giải pháp của bạn hơn 6 câu trả lời còn lại?
divibisan

1
@vino vui lòng thêm một số lời giải thích
Nursnaaz

-1

Bạn cũng có thể sử dụng "dataframe or table_name".apply(lambda x: x.fillna(x.mean())).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.