¿Cómo aplicar orden de columna personalizada a pandas boxplot?

Puedo obtener una gráfica de caja de una columna de salario en un dataframe de pandas

train.boxplot(column='PredictionError',by='Category',sym='') 

… sin embargo, no puedo averiguar cómo definir el orden de índice usado en la columna ‘Categoría’ – quiero proporcionar mi propio pedido personalizado, de acuerdo con otro criterio:

 category_order_by_mean_salary = train.groupby('Category')['Salary'].mean().order().keys() 

¿Cómo puedo aplicar mi orden de columna personalizada a las columnas de diagtwig de caja? (aparte de los feos kludging los nombres de columna con un prefijo para forzar ordenación)

‘Categoría’ es una columna de cadena que toma 27 valores distintos: ['Accounting & Finance Jobs','Admin Jobs',...,'Travel Jobs'] . Por lo tanto, se puede factorizar fácilmente con pd.Categorical.from_array()

En la inspección, la limitación está dentro de pandas.tools.plotting.py:boxplot() , que convierte el objeto de columna sin permitir el pedido:

  • pandas.core.frame.py.boxplot () es un passthrough a
  • pandas.tools.plotting.py:boxplot () que crea instancias …
  • matplotlib.pyplot.py:boxplot () que crea instancias …
  • matplotlib.axes.py:boxplot ()

Supongo que podría hackear una versión personalizada de pandas boxplot (), o acceder a las partes internas del objeto. Y también presentar una solicitud de mejora.

EDITAR: esta pregunta surgió con pandas ~ 0.13 y probablemente ha quedado obsoleta por las versiones recientes (¿0.19+?) Según la respuesta tardía de @Cireo.

Es difícil decir cómo hacer esto sin un ejemplo de trabajo. Mi primera suposición sería simplemente agregar una columna de entero con las órdenes que desee.

Una forma simple y de fuerza bruta sería sumr cada diagtwig de caja de uno en uno.

 import numpy as np import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame(np.random.rand(37,4), columns=list('ABCD')) columns_my_order = ['C', 'A', 'D', 'B'] fig, ax = plt.subplots() for position, column in enumerate(columns_my_order): ax.boxplot(df[column], positions=[position]) ax.set_xticks(range(position+1)) ax.set_xticklabels(columns_my_order) ax.set_xlim(xmin=-0.5) plt.show() 

introduzca la descripción de la imagen aquí

En realidad me quedé estancado con la misma pregunta. Y lo resolví haciendo un mapa y reinicié las tags xticklabels, con el siguiente código:

 df = pd.DataFrame({"A":["d","c","d","c",'d','c','a','c','a','c','a','c']}) df['val']=(np.random.rand(12)) df['B']=df['A'].replace({'d':'0','c':'1','a':'2'}) ax=df.boxplot(column='val',by='B') ax.set_xticklabels(list('dca')) 

Tenga en cuenta que los pandas ahora pueden crear columnas categóricas. Si no le importa tener todas las columnas presentes en su gráfico, o recortarlas adecuadamente, puede hacer algo como lo siguiente:

http://pandas.pydata.org/pandas-docs/stable/categorical.html

 df['Category'] = df['Category'].astype('category', ordered=True) 

Los pandas recientes también parecen permitir que las positions pasen completamente desde el marco hasta los ejes.

Agregando una respuesta por separado, que quizás podría ser otra pregunta, se agradecen los comentarios.

Quería agregar un orden de columna personalizado dentro de un groupby, lo que me planteaba muchos problemas. Al final, tuve que evitar tratar de usar el boxplot de boxplot de un objeto groupby , y en lugar de eso, groupby cada argumento secundario para proporcionar posiciones explícitas.

 import matplotlib.pyplot as plt import pandas as pd df = pd.DataFrame() df['GroupBy'] = ['g1', 'g2', 'g3', 'g4'] * 6 df['PlotBy'] = [chr(ord('A') + i) for i in xrange(24)] df['SortBy'] = list(reversed(range(24))) df['Data'] = [i * 10 for i in xrange(24)] # Note that this has no effect on the boxplot df = df.sort_values(['GroupBy', 'SortBy']) for group, info in df.groupby('GroupBy'): print 'Group: %r\n%s\n' % (group, info) # With the below, cannot use # - sort data beforehand (not preserved, can't access in groupby) # - categorical (not all present in every chart) # - positional (different lengths and sort orders per group) # df.groupby('GroupBy').boxplot(layout=(1, 5), column=['Data'], by=['PlotBy']) fig, axes = plt.subplots(1, df.GroupBy.nunique(), sharey=True) for ax, (g, d) in zip(axes, df.groupby('GroupBy')): d.boxplot(column=['Data'], by=['PlotBy'], ax=ax, positions=d.index.values) plt.show() 

Dentro de mi código final, fue incluso un poco más complicado determinar las posiciones porque tenía múltiples puntos de datos para cada valor de clasificación, y terminé teniendo que hacer lo siguiente:

 to_plot = data.sort_values([sort_col]).groupby(group_col) for ax, (group, group_data) in zip(axes, to_plot): # Use existing sorting ordering = enumerate(group_data[sort_col].unique()) positions = [ind for val, ind in sorted((v, i) for (i, v) in ordering)] ax = group_data.boxplot(column=[col], by=[plot_by], ax=ax, positions=positions)