NaN (결측) 값이있는 pandas GroupBy 열


147

그룹화하려는 열에 누락 된 값이 많은 DataFrame이 있습니다.

import pandas as pd
import numpy as np
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})

In [4]: df.groupby('b').groups
Out[4]: {'4': [0], '6': [2]}

Pandas가 NaN 대상 값으로 행을 삭제했음을 참조하십시오. (이 행을 포함하고 싶습니다!)

많은 연산 (많은 col이 누락 된 값을 가짐)이 필요하고 중간 (일반적으로 임의의 포리스트)보다 복잡한 함수를 사용하기 때문에 너무 복잡한 코드 작성을 피하고 싶습니다.

어떤 제안? 이를 위해 함수를 작성해야합니까 아니면 간단한 해결책이 있습니까?


1
@PhillipCloud 나는 Jeff의 오픈 팬더 향상 과 관련하여 실제로 좋은 질문을 포함하도록이 질문을 편집했습니다 .
Andy Hayden

1
그룹에 NaN을 포함 (및 전파) 할 수없는 것은 상당히 악화되고 있습니다. 이 행동이 다른 많은 것들과 일치하지 않기 때문에 인용 R은 설득력이 없습니다. 어쨌든 더미 핵도 꽤 나쁘다. 그러나 NaN이 있으면 그룹의 크기 (NaN 포함) 및 개수 (NaN 무시)가 달라집니다. dfgrouped = df.groupby ([ 'b']). a.agg ([ 'sum', 'size', 'count']) dfgrouped [ 'sum'] [dfgrouped [ 'size']! = dfgrouped [ 'count ']] = 없음
Brian Preslopsky

구체적으로 달성하려는 것을 요약 할 수 있습니까? 즉, 출력을 볼 수 있지만 "원하는"출력은 무엇입니까?
ca

2
팬더 1.1 당신은 곧 지정할 수 있습니다 dropna=False에서 groupby()원하는 결과를 얻을 수 있습니다. 추가 정보
cs95

답변:


130

이것은 문서의 데이터 누락 섹션에 언급되어 있습니다 .

GroupBy의 NA 그룹은 자동으로 제외됩니다. 예를 들어이 동작은 R과 일치합니다.

한 가지 해결 방법은 그룹화를 수행하기 전에 자리 표시자를 사용하는 것입니다 (예 : -1).

In [11]: df.fillna(-1)
Out[11]: 
   a   b
0  1   4
1  2  -1
2  3   6

In [12]: df.fillna(-1).groupby('b').sum()
Out[12]: 
    a
b    
-1  2
4   1
6   3

즉, 이것은 꽤 끔찍한 해킹을 느낍니다. 아마도 NaN을 그룹별로 포함시키는 옵션이 있어야합니다 ( 이 같은 깃 허브 문제 -동일한 자리 표시 자 해킹을 사용합니다).


4
이것은 이전에 생각했던 논리적이지만 재미있는 솔루션입니다 .Pandas는 NaN 필드를 빈 필드로 만들고 다시 변경해야합니다. 이것은 SQL 서버를 실행하고 거기에서 테이블을 쿼리하거나 (너무 복잡해 보입니다), Pandas에도 불구하고 다른 라이브러리를 찾거나 내 자신을 사용하는 등의 다른 솔루션을 찾고 있거나 제거하기 위해). Thx
Gyula Sámuel Karli

@ GyulaSámuelKarli 나에게 이것은 작은 버그 인 것 같으며 (위의 버그 보고서 참조) 내 솔루션은 해결 방법입니다. 도서관 전체를 써서 이상하게 생각합니다.
Andy Hayden

1
나는 판다를 쓰고 싶지 않다. 단지 내 요청에 가장 적합한 도구를 찾으십시오.
Gyula Sámuel Karli

1
아래 답변을 살펴보면 꽤 좋은 (더 깨끗하고 아마도 더 빠른) 해결책을 찾았습니다. stackoverflow.com/a/43375020/408853
ca

4
아니요, 이것은 R과 일치하지 않습니다. df %> % group_by는 NA 요약을 경고와 함께 제공하여 fct_explicit_na를 통해 그룹화 열을 전달하면 피할 수 있으며 (Missing) 레벨이 작성됩니다.
Ravaging Care

40

고대 주제, 누군가 여전히 이것을 우연히 발견하면 다른 해결 방법은 그룹화하기 전에 .astype (str)을 통해 문자열로 변환하는 것입니다. 그것은 NaN을 보존 할 것입니다.

in:
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})
df['b'] = df['b'].astype(str)
df.groupby(['b']).sum()
out:
    a
b   
4   1
6   3
nan 2

@ K3 --- rnc : 링크에 대한 주석보기-링크의 게시물 작성자가 잘못했습니다.
Thomas

@Thomas, 예, 위의 예에서와 동일합니다. 예제를 안전하고 사소한 것으로 만들 수 있으면 수정하십시오.
K3 --- rnc

sum의는 a여기에 문자열 연결이 아닌 숫자 합계입니다. 'b'는 별개의 항목으로 구성 되었기 때문에 "작동"합니다. 'a'는 숫자이고 'b'는 문자열이어야합니다.
BallpointBen

28

팬더> = 1.1

pandas 1.1 에서이 동작을보다 잘 제어 할 수 있으므로 NA 값은dropna=False 다음을 사용 하여 그룹화에서 허용됩니다 .

pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'

# Example from the docs
df

   a    b  c
0  1  2.0  3
1  1  NaN  4
2  2  1.0  3
3  1  2.0  2

# without NA (the default)
df.groupby('b').sum()

     a  c
b        
1.0  2  3
2.0  2  5
# with NA
df.groupby('b', dropna=False).sum()

     a  c
b        
1.0  2  3
2.0  2  5
NaN  1  4

4
이 답변이 점차적으로 정상에 오르기를 바랍니다. 올바른 접근 방식입니다.
kdbanman

1.1이 아직 출시되지 않았다고 생각합니다. conda와 pip를 확인했으며 버전은 여전히 ​​1.0.4입니다.
sammywemmy

1
@sammywemmy 예. 현재로서는 개발 환경 에서만 실행할 수 있습니다 . 오래된 SO 게시물에 새로운 기능을 도입 할 때 헤드 스타트를하고 싶습니다. ;-)
cs95

9

평판이 충분하지 않기 때문에 M. Kiewisch에 의견을 추가 할 수 없습니다 (41 만 있고 50 명 이상이 필요합니다).

어쨌든 M. Kiewisch 솔루션은 그대로 작동하지 않으며 더 많은 조정이 필요할 수 있음을 지적하고 싶습니다. 예를 들어 고려

>>> df = pd.DataFrame({'a': [1, 2, 3, 5], 'b': [4, np.NaN, 6, 4]})
>>> df
   a    b
0  1  4.0
1  2  NaN
2  3  6.0
3  5  4.0
>>> df.groupby(['b']).sum()
     a
b
4.0  6
6.0  3
>>> df.astype(str).groupby(['b']).sum()
      a
b
4.0  15
6.0   3
nan   2

그룹 b = 4.0의 경우 해당 값은 6 대신 15입니다. 여기서는 숫자로 추가하는 대신 1과 5를 문자열로 연결합니다.


12
그 이유는 b열 대신 DF 전체를 str로 변환했기 때문입니다.
Korem

이것은 지금 언급 한 답변에서 수정되었습니다.
Shaido-복원 모니카

1
새로운 해결책은 더 낫지 만 여전히 안전하지는 않습니다. 열 'b'의 항목 중 하나가 문자열 화 된 np.NaN과 동일한 경우를 고려하십시오. 그런 다음 그 것들이 함께 클럽입니다. df = pd.DataFrame ({ 'a': [1, 2, 3, 5, 6], 'b': [ 'foo', np.NaN, 'bar', 'foo', 'nan']}) ; df [ 'b'] = df [ 'b']. astype (str); df.groupby ([ 'b']). sum ()
Kamaraju Kusumanchi

6

앤디 헤이든의 솔루션을 하나의 작은 점 - 있기 때문에 (? 이상) 작동하지 않습니다 np.nan == np.nan수익률 False, 너무 replace기능이 실제로 아무것도하지 않습니다.

나를 위해 일한 것은 이것이었다 :

df['b'] = df['b'].apply(lambda x: x if not np.isnan(x) else -1)

(적어도 이것이 팬더 0.19.2의 동작입니다. 다른 답변으로 추가하여 죄송합니다. 의견을 말할만한 평판이 없습니다.)


12
또한 있습니다 df['b'].fillna(-1).
K3 --- rnc

6

지금까지 제공된 모든 답변은 실제로 데이터 세트의 일부인 더미 값을 선택할 수 있으므로 잠재적으로 위험한 행동을 초래합니다. 많은 속성을 가진 그룹을 만들면 점점 더 커질 것입니다. 간단히 말해서, 접근 방식이 항상 잘 일반화되는 것은 아닙니다.

덜 해킹 된 해결 방법은 pd.drop_duplicates ()를 사용하여 각각 고유 한 ID를 가진 고유 한 값 조합 인덱스를 만든 다음 해당 ID를 그룹화하는 것입니다. 더 장황하지만 작업을 수행합니다.

def safe_groupby(df, group_cols, agg_dict):
    # set name of group col to unique value
    group_id = 'group_id'
    while group_id in df.columns:
        group_id += 'x'
    # get final order of columns
    agg_col_order = (group_cols + list(agg_dict.keys()))
    # create unique index of grouped values
    group_idx = df[group_cols].drop_duplicates()
    group_idx[group_id] = np.arange(group_idx.shape[0])
    # merge unique index on dataframe
    df = df.merge(group_idx, on=group_cols)
    # group dataframe on group id and aggregate values
    df_agg = df.groupby(group_id, as_index=True)\
               .agg(agg_dict)
    # merge grouped value index to results of aggregation
    df_agg = group_idx.set_index(group_id).join(df_agg)
    # rename index
    df_agg.index.name = None
    # return reordered columns
    return df_agg[agg_col_order]

이제 다음을 간단히 수행 할 수 있습니다.

data_block = [np.tile([None, 'A'], 3),
              np.repeat(['B', 'C'], 3),
              [1] * (2 * 3)]

col_names = ['col_a', 'col_b', 'value']

test_df = pd.DataFrame(data_block, index=col_names).T

grouped_df = safe_groupby(test_df, ['col_a', 'col_b'],
                          OrderedDict([('value', 'sum')]))

이렇게하면 더미 값으로 오인 된 실제 데이터를 덮어 쓰지 않아도됩니다.


이것은 일반적인 경우에 가장 적합한 솔루션이지만 대신 사용할 수있는 잘못된 문자열 / 숫자를 알고있는 경우 Andy Hayden의 대답과 함께 갈 것입니다 ... 팬더가 곧이 문제를 해결하기를 바랍니다.
Sarah Messer

4

나는 이것에 이미 대답했지만 어떤 이유로 대답이 주석으로 변환되었습니다. 그럼에도 불구하고 이것이 가장 효율적인 솔루션입니다.

그룹에 NaN을 포함 (및 전파) 할 수없는 것은 상당히 악화되고 있습니다. 이 행동이 다른 많은 것들과 일치하지 않기 때문에 인용 R은 설득력이 없습니다. 어쨌든 더미 핵도 꽤 나쁘다. 그러나 NaN이 있으면 그룹의 크기 (NaN 포함) 및 개수 (NaN 무시)가 달라집니다.

dfgrouped = df.groupby(['b']).a.agg(['sum','size','count'])

dfgrouped['sum'][dfgrouped['size']!=dfgrouped['count']] = None

서로 다른 경우 해당 그룹의 집계 함수 결과에 대해 값을 다시 없음으로 설정할 수 있습니다.


1
이것은 나에게 매우 도움이되었지만 원래 질문과 약간 다른 질문에 대답합니다. IIUC, 솔루션은 요약에서 NaN을 전파하지만 "b"열의 NaN 항목은 여전히 ​​행으로 삭제됩니다.
Andrew

0

아나콘다에 Pandas 1.1 설치

cs95의 답변에 대해서는 언급 할 수 없지만 그는 문제를 해결하는 데 도움이되었습니다.

Pandas 1.1을 설치하려고 시도했지만 그의 코드를 사용하지 못해서 Google을 설치하고 설치할 수있었습니다.

먼저 관리자 로 anaconda 프롬프트를 실행 하고 다음 코드를 붙여 넣습니다.

pip install pandas==1.1.0rc0

그 후 사용 포함 dropna = False

링크 : https://libraries.io/pypi/pandas


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.