Option 1
def first_last(df): return df.ix[[0, -1]] df.groupby(level=0, group_keys=False).apply(first_last)

Option 2 - only works if the index is unique
idx = df.index.to_series().groupby(level=0).agg(['first', 'last']).stack() df.loc[idx]
Option 3 - in the notes below, this only makes sense when there is no NA
I also abused the agg function. The code below works, but much uglier.
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])
Note
per @unutbu: agg(['first', 'last']) accept values ββother than na.
I interpreted this as, then it will be necessary to run this column column by column. In addition, forcing the alignment of the index level = 1 may not even make sense.
Include another test
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])

Of course! This second decision takes the first valid value in column X. Now it makes no sense to make this value align with index a.
piRSquared
source share