development

NaN (결측) 값이있는 그룹 별 열

big-blog 2020. 7. 28. 07:29
반응형

NaN (결측) 값이있는 그룹 별 열


그룹화하려는 열에 누락 된 값이 많은 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이 누락 된 값을 가짐)이 필요하고 중간 (일반적으로 임의의 포리스트)보다 복잡한 함수를 사용하기 때문에 너무 복잡한 코드 작성을 피하고 싶습니다.

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


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

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을 그룹별로 포함시키는 옵션이 있어야합니다 ( 이 같은 깃 허브 문제 -동일한 자리 표시 자 해킹을 사용합니다).


고대 주제, 누군가 여전히 이것을 우연히 발견하면 다른 해결 방법은 그룹화하기 전에 .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

평판이 충분하지 않기 때문에 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를 문자열로 연결합니다.


One small point to Andy Hayden's solution – it doesn't work (anymore?) because np.nan == np.nan yields False, so the replace function doesn't actually do anything.

What worked for me was this:

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

(At least that's the behavior for Pandas 0.19.2. Sorry to add it as a different answer, I do not have enough reputation to comment.)


All answers provided thus far result in potentially dangerous behavior as it is quite possible you select a dummy value that is actually part of the dataset. This is increasingly likely as you create groups with many attributes. Simply put, the approach doesn't always generalize well.

A less hacky solve is to use pd.drop_duplicates() to create a unique index of value combinations each with their own ID, and then group on that id. It is more verbose but does get the job done:

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]

Note that you can now simply do the following:

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')]))

This will return the successful result without having to worry about overwriting real data that is mistaken as a dummy value.


I answered this already, but some reason the answer was converted to a comment. Nevertheless, this is the most efficient solution:

There not being able to include (and propagate) NaNs in groups is quite aggravating. Citing R is not convincing, as this behavior is not consistent with a lot of other things. Anyway, the dummy hack is also pretty bad. However, the size (includes NaNs) and the count (ignores NaNs) of a group will differ if there are NaNs.

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

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

When these differ, you can set the value back to the None for the result of the aggregation function for that group.

참고URL : https://stackoverflow.com/questions/18429491/groupby-columns-with-nan-missing-values

반응형