В pandas есть что-то вроде GroupBy.get_group, но с необязательным значением по умолчанию?

У меня есть DataFrame df, который я "groupby'ed". Я ищу функцию, которая похожа на get_group (name), за исключением того, что вместо того, чтобы бросать KeyError, если имя не существует, возвращает пустой DataFrame (или какое-то другое значение), похожее на то, как работает dict.get:

g = df.groupby('x')

# doesn't work, but would be nice:
i = g.get_group(1, default=[])

# does work, but is hard to read:
i = g.obj.take(g.indices.get(1, []), g.axis)

Есть ли уже функция, которая предоставляет это?

Edit:

Во многих отношениях объект GroupBy представлен dict (.indicies,.groups), и эта функциональность "получить с дефолтом" была достаточно ясной для концепции dict, что она включена в сам язык Python. Казалось, что если у диктоподобной вещи нет дефолта, возможно, я не понимаю ее правильно? Почему у диктофона вроде бы нет "получить с дефолтом"?

Сокращенный пример того, что я хочу сделать, это:

df1_bymid = df1.groupby('mid')
df2_bymid = df2.groupby('mid')

for mid in set(df1_bymid.groups) | set(df2_bymid.groups) :
    rows1 = df1_bymid.get_group(mid, [])
    rows2 = df1_bymid.get_group(mid, [])
    for row1, row2 in itertools.product(rows1, rows2) :
        yield row1, row2

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

def get_group(df, name, obj=None, default=None) :
    if obj is None :
        obj = df.obj

    try :
        inds = df.indices[name]
    except KeyError, e :
        if default is None :
            raise e

        inds = default

    return df.obj.take(inds, df.axis)

Ответ 1

Я мог бы определить свой собственный get_group() следующим образом

In [55]: def get_group(g, key):
   ....:     if key in g.groups: return g.get_group(key)
   ....:     return pd.DataFrame()
   ....: 

In [52]: get_group(g, 's1')
Out[52]: 
   Mt Sp  Value  count
0  s1  a      1      3
1  s1  b      2      2

In [54]: get_group(g, 's4')
Out[54]: 
Empty DataFrame
Columns: []
Index: []   

Ответ 2

Это не так красиво, но вы можете сделать что-то вроде этого:

Настройка:

>>> df = pandas.DataFrame([[1,2,3],[4,5,6],[1,8,9]], columns=['a','b','c'])
>>> df
   a  b  c
0  1  2  3
1  4  5  6
2  1  8  9
>>> g = df.groupby('a')

Теперь g.get_group требует, чтобы переданный ключ существовал в базовом groups dict, но вы могли получить доступ к этому элементу самостоятельно, и на самом деле это обычный питон dict. Он берет групповое значение для набора индексов:

>>> g.groups
{1: Int64Index([0, 2], dtype='int64'), 4: Int64Index([1], dtype='int64')}
>>> type(g.groups)
<type 'dict'>

Если вы используете эти возвращенные индексы в функции определения местоположения блока данных, вы можете получить свои группы так же, как get_group:

>>> df.loc[g.groups[1]]
   a  b  c
0  1  2  3
2  1  8  9

Так как groups является dict, вы можете использовать метод get. Без предоставления значения по умолчанию это вернет None, что приведет к тому, что loc вызовет исключение. Но он примет пустой список:

>>> df.loc[g.groups.get(1, [])]
   a  b  c
0  1  2  3
2  1  8  9
>>> df.loc[g.groups.get(2, [])]
Empty DataFrame
Columns: [a, b, c]
Index: []

Это не так чисто, как предоставление значения по умолчанию get_group (возможно, они должны добавить эту функцию в будущую версию), но она работает.

Ответ 3

Вы можете использовать defaultdict для достижения этой цели.

Допустим, у вас есть объект groupby, который разбивает данные на столбец, который больше нуля. Проблема в том, что все значения могут быть больше или меньше нуля, а это означает, что вы не можете быть уверены, что в групповом режиме доступны 1 или 2 кадра данных.

g_df = df.groupby(df.some_column.gt(0))  

Тогда есть 2 подхода

df_dict  = defaultdict(pd.DataFrame, {i:i_df for i,i_df in g_df} )
df_dict[True]
df_dict[False]                                                                                                                                                                                                         

Или же:

df_dict  = defaultdict(list, g_df.groups)                                                                                                                                                                                                                                      
df.loc[df_dict[True]]
df.loc[df_dict[False]]

Я не проверял, что является более эффективным, очевидно, что второй подход создает дефолт только для индекса, а не для фрейма данных - так что может быть более эффективным.