Получить первое и последнее значение в группе

У меня есть dataframe df

df = pd.DataFrame(np.arange(20).reshape(10, -1),
                  [['a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd'],
                   ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']],
                  ['X', 'Y'])

Как получить первую и последнюю строки, сгруппированные по первому уровню индекса?

Я пробовал

df.groupby(level=0).agg(['first', 'last']).stack()

и получил

          X   Y
a first   0   1
  last    6   7
b first   8   9
  last   12  13
c first  14  15
  last   16  17
d first  18  19
  last   18  19

Это так близко к тому, что я хочу. Как сохранить индекс уровня 1 и получить его вместо этого:

      X   Y
a a   0   1
  d   6   7
b e   8   9
  g  12  13
c h  14  15
  i  16  17
d j  18  19
  j  18  19

Ответ 1

Вариант 1

def first_last(df):
    return df.ix[[0, -1]]

df.groupby(level=0, group_keys=False).apply(first_last)

введите описание изображения здесь


Вариант 2 - работает только если индекс уникален

idx = df.index.to_series().groupby(level=0).agg(['first', 'last']).stack()
df.loc[idx]

Вариант 3 - в примечаниях ниже, это имеет смысл только тогда, когда нет NA

Я также злоупотреблял функцией agg. Код ниже работает, но намного уродливее.

df.reset_index(1).groupby(level=0).agg(['first', 'last']).stack() \
    .set_index('level_1', append=True).reset_index(1, drop=True) \
    .rename_axis([None, None])

Примечание

per @unutbu: agg(['first', 'last']) принимать значения, отличные от na.

Я интерпретировал это как, тогда необходимо будет запустить этот столбец по столбцу. Кроме того, принудительное выравнивание уровня индекса = 1 может даже не иметь смысла.

Включить еще один тест

df = pd.DataFrame(np.arange(20).reshape(10, -1),
                  [list('aaaabbbccd'),
                   list('abcdefghij')],
                  list('XY'))

df.loc[tuple('aa'), 'X'] = np.nan

def first_last(df):
    return df.ix[[0, -1]]

df.groupby(level=0, group_keys=False).apply(first_last)

введите описание изображения здесь

df.reset_index(1).groupby(level=0).agg(['first', 'last']).stack() \
    .set_index('level_1', append=True).reset_index(1, drop=True) \
    .rename_axis([None, None])

введите описание изображения здесь

Конечно же! Это второе решение принимает первое допустимое значение в столбце X. Теперь бессмысленно заставить это значение выровняться с индексом a.

Ответ 2

Это может быть одним из простых решений.

df.groupby(level = 0, as_index= False).nth([0,-1])

      X   Y
a a   0   1
  d   6   7
b e   8   9
  g  12  13
c h  14  15
  i  16  17
d j  18  19

Надеюсь это поможет. (Y),

Ответ 3

Пожалуйста, попробуйте это:

Для последнего значения: df.groupby('Column_name').nth(-1),

Для первого значения: df.groupby('Column_name').nth(0)