Pandas : 각 그룹에서 평균으로 결 측값 채우기


88

이것은 간단해야하지만 내가 찾은 가장 가까운 것은이 게시물입니다 : pandas : Filling missing values ​​within a group , 그리고 여전히 내 문제를 해결할 수 없습니다 ....

다음 데이터 프레임이 있다고 가정합니다.

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

무리 감사.

답변:


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

또한 Wes McKinney 책을 찾으십시오. 개인적으로 groupby의 문서는 비정상적이라고 생각하며 책은 약간 더 좋습니다.
Woody Pride

38
3 개 이상의 열이있는 경우 열 이름을 지정해야합니다. df [ "value"] = df.groupby ( "name"). transform (lambda x : x.fillna (x.mean ())) [ 'value ']
Lauren

16
@Lauren 좋은 지적입니다. 성능상의 이유로 값 열 사양을 group-by 절로 더 왼쪽으로 이동하는 것을 고려할 수 있다는 점을 추가하고 싶습니다. 이런 식으로 람다 함수는 모든 열이 아닌 특정 열의 값에 대해서만 호출 된 다음 열을 선택합니다. 테스트를했는데 두 개의 컬럼을 사용할 때 두 배나 빨랐습니다. 그리고 당연히 당신은 당신이 전가 할 필요가 없습니다 더 많은 열이 더 나은 성능을 얻을 :df["value"] = df.groupby("name")["value"].transform(lambda x: x.fillna(x.mean()))
앙드레 C. 앤더슨에게

나는 이틀 동안 이것을 찾고 있었다 .. 단지 당신을위한 질문. 루프로이 작업을 수행하는 것이 왜 너무 어렵습니까? 제 경우에는 두 개의 다중 인덱스가 있습니다. 즉 State, Age_Group그룹의 결 측값을 그룹 평균으로 채우려 고합니다 (동일한 연령 그룹 내의 동일한 주에서 평균을 취하고 그룹의 결 측값을 채 웁니다) .. 감사합니다
결 측값을 Ozkan Serttas

51

fillna+ groupby+ transform+mean

이것은 직관적 인 것 같습니다.

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

그만큼 groupby+의 transform구문은 원래 dataframe의 인덱스에 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

이 일반화 경우에 우리에 의해 그룹 싶습니다 category하고 name, 만에 전가value .

다음과 같이 해결할 수 있습니다.

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

group-by 절의 열 목록을 확인하고 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 19

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"을 사용하는 것과 관련이 있습니다.

그러나 나는 나에게 더 직관적 인 결 측값을 채우기 위해 "apply"와 함께 "groupby"를 사용하는 것을 선호합니다.

>>> 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']... 나는이 제안 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
귀하의 답변에 대해 설명해주십시오. Google 에서이 페이지를 우연히 발견 한 사람이 다른 6 가지 답변보다 솔루션을 사용해야하는 이유는 무엇입니까?
divibisan

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.