¿Cómo puedo ordenar las particiones definidas por una columna pero dejar las particiones donde están?

Considere el df dataframe

 df = pd.DataFrame(dict( A=list('XXYYXXYY'), B=range(8, 0, -1) )) print(df) AB 0 X 8 1 X 7 2 Y 6 3 Y 5 4 X 4 5 X 3 6 Y 2 7 Y 1 

Con el grupo 'X' definido por la columna 'A' , quiero ordenar [8, 7, 4, 3] al esperado [3, 4, 7, 8] . Sin embargo, quiero dejar esas filas donde están.

  AB 5 X 3 <-- Notice all X are in same positions 4 X 4 <-- However, `[3, 4, 7, 8]` have shifted 7 Y 1 6 Y 2 1 X 7 <-- 0 X 8 <-- 3 Y 5 2 Y 6 

Puede usar la transform para recuperar su nuevo orden de índice deseado, luego usar reindex para reordenar su DataFrame:

 # Use transform to return the new ordered index values. new_idx = df.groupby('A')['B'].transform(lambda grp: grp.sort_values().index) # Reindex. df = df.reindex(new_idx.rename(None)) 

Podría combinar las dos líneas de arriba en una línea larga, si así lo desea.

La salida resultante:

  AB 5 X 3 4 X 4 7 Y 1 6 Y 2 1 X 7 0 X 8 3 Y 5 2 Y 6 

Tenga en cuenta que si no le importa mantener su índice anterior, puede reasignar directamente desde la transform :

 df['B'] = df.groupby('A')['B'].transform(lambda grp: grp.sort_values()) 

Cuyos rendimientos:

  AB 0 X 3 1 X 4 2 Y 1 3 Y 2 4 X 7 5 X 8 6 Y 5 7 Y 6 

La única forma en que pensé cómo resolver esto de manera eficiente fue clasificar dos veces y desconectar una vez.

 v = df.values # argsort just first column with kind='mergesort' to preserve subgroup order a1 = v[:, 0].argsort(kind='mergesort') # Fill in an un-sort array to unwind the `a1` argsort a_ = np.empty_like(a1) a_[a1] = np.arange(len(a1)) # argsort by both columns... not exactly what I want, yet. a2 = np.lexsort(vT[::-1]) # Sort with `a2` then unwind the first layer with `a_` pd.DataFrame(v[a2][a_], df.index[a2][a_], df.columns) AB 5 X 3 4 X 4 7 Y 1 6 Y 2 1 X 7 0 X 8 3 Y 5 2 Y 6 

Pruebas

Código

 def np_intra_sort(df): v = df.values a1 = v[:, 0].argsort(kind='mergesort') a_ = np.empty_like(a1) a_[a1] = np.arange(len(a1)) a2 = np.lexsort(vT[::-1]) return pd.DataFrame(v[a2][a_], df.index[a2][a_], df.columns) def pd_intra_sort(df): def sub_sort(x): return x.sort_values().index idx = df.groupby('A').B.transform(sub_sort).values return df.reindex(idx) 

Pequeños datos

Introduzca la descripción de la imagen aquí

Datos grandes

 df = pd.DataFrame(dict( A=list('XXYYXXYY') * 10000, B=range(8 * 10000, 0, -1) )) 

Introduzca la descripción de la imagen aquí