Панды означают функцию, которая работает со строками - PullRequest
0 голосов
/ 03 сентября 2018

Мне нужны функции агрегирования (mean, std, var, min, max и т. Д.), Которые работают с фреймом данных Pandas, могут вызываться из groupby (). Apply (), но не удаляет строки, если все их значения одинаковы .

Итак:

mean(['a','a']) должно дать 'a'.

mean(['a','b']) должен дать NaN.

Поведение панд (как и следовало ожидать) - отбрасывать нечисловые значения:

>>> df = pd.DataFrame({'c1':  [1,2,3],
                       'c2':  [1,1,1],
                       'c3':  ['a','b','c'],
                       'c4':  ['a','a','a'],
                       'cat': ['x','x','y']})
>>> df.mean()
c1    2.0
c2    1.0

Где мне нужно:

c1    2.0
c2    1.0
c3    NaN
c4    'a'
cat   NaN

Мне также нужны эти функции для каждой группы. Поэтому я создал функцию, которая будет выводить строки, как указано выше, аналогично значению (), которое я затем смогу вызвать из groupby.apply ():

def str_reduce(df):
    is_uniq = False
    try:
        is_uniq = df.unique().size == 1
    except:
        pass
    return df[0] if is_uniq else np.NaN

Это работает аналогично значению, когда я применяю его напрямую:

>>> df[['c3','c4']].apply(str_reduce)
c3    NaN
c4      a

Однако теперь он дает неожиданный результат, когда я вызываю его вслед за групповым?

>>> df.groupby(['cat']).apply(str_reduce) 
cat
x   NaN
y   NaN

Что я делаю не так? И / или есть ли лучший / более простой способ сделать это в Pandas?

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Помогает ли это каким-либо образом?

list_columns=df.columns.tolist()
dict_new={}
for i in range(len(list_columns)):
    if(df[list_columns[i]].dtype)=='O':
        list_of_items=list(set(df[list_columns[i]].tolist()))
        if(len(list_of_items)>1):
            dict_new[list_columns[i]]='NaN'
        else:
            dict_new[list_columns[i]]=list_of_items[0]
    elif(df[list_columns[i]].dtype)=='int64':
        dict_new[list_columns[i]]=df[list_columns[i]].mean()

s = pd.Series(dict_new, name='ValueColumn')
test=pd.DataFrame(s)
test.reset_index(inplace=True)


index   ValueColumn
0   c1  2
1   c2  1
2   c3  NaN
3   c4  a
4   cat NaN

Вы можете переименовать столбец с именем index здесь, что вы хотите.

0 голосов
/ 03 сентября 2018

Когда вы используете .apply в групповом режиме, он пытается применить функцию ко всему сгруппированному объекту. В этом случае вы хотите применить функцию к каждому столбцу в каждой группе, поэтому использование .agg гораздо более уместно и даст ожидаемый результат.

Я думаю, что написанная вами функция не совсем то, что вам нужно, потому что вы должны знать, какие столбцы являются числовыми заранее, иначе она не будет работать должным образом для числовых столбцов. Вместо этого вы можете написать функции, которые проверяют, является ли dtype числовым, и действуют как обычно, в противном случае они применяют ваше специальное правило.

import pandas as pd
import numpy as np
from pandas.api.types import is_numeric_dtype

def mean_str(col):
    if is_numeric_dtype(col):
        return col.mean()
    else:
        return col.unique() if col.nunique() == 1 else np.NaN

Так что теперь вы бы сделали что-то вроде:

df.groupby('cat').agg(mean_str)

Выход:

      c1  c2   c3 c4
cat                 
x    1.5   1  NaN  a
y    3.0   1    c  a
...