Seaborn: countplot () con frecuencias

Tengo un Panda DataFrame con una columna llamada “AXLES”, que puede tomar un valor entero entre 3-12. Estoy tratando de usar la opción de cuenta de punta de Seaborn () para lograr la siguiente gráfica:

  1. el eje y izquierdo muestra las frecuencias de estos valores que ocurren en los datos. El eje se extiende son [0% -100%], marcas de graduación en cada 10%.
  2. el eje y derecho muestra los recuentos reales, los valores corresponden a las marcas determinadas por el eje y izquierdo (marcados cada 10%).
  3. El eje x muestra las categorías para los diagtwigs de barras [3, 4, 5, 6, 7, 8, 9, 10, 11, 12].
  4. La anotación en la parte superior de las barras muestra el porcentaje real de esa categoría.

El siguiente código me da la gráfica a continuación, con conteos reales, pero no pude encontrar una manera de convertirlos en frecuencias. Puedo obtener las frecuencias usando df.AXLES.value_counts()/len(df.index) pero no estoy seguro acerca de cómo insertar esta información en el countplot() de Seaborn countplot() .

También encontré una solución para las anotaciones, pero no estoy seguro de si es la mejor implementación.

¡Cualquier ayuda sería apreciada!

Gracias

 plt.figure(figsize=(12,8)) ax = sns.countplot(x="AXLES", data=dfWIM, order=[3,4,5,6,7,8,9,10,11,12]) plt.title('Distribution of Truck Configurations') plt.xlabel('Number of Axles') plt.ylabel('Frequency [%]') for p in ax.patches: ax.annotate('%{:.1f}'.format(p.get_height()), (p.get_x()+0.1, p.get_height()+50)) 

enter image description here

EDITAR:

Me acerqué a lo que necesito con el siguiente código, usando el diagtwig de barras de Pandas, abandonando a Seaborn. Se siente como si estuviera usando tantas soluciones, y tiene que haber una forma más fácil de hacerlo. Los problemas con este enfoque:

  • No existe una palabra clave de order en la función de representación gráfica de barras de Pandas como la cuenta de puntos de Seaborn (), por lo que no puedo trazar todas las categorías de 3 a 12 como lo hice en la cuenta de puntos (). Necesito que se muestren incluso si no hay datos en esa categoría.
  • El eje y secundario arruina las barras y la anotación por alguna razón (vea las líneas de cuadrícula blancas dibujadas sobre el texto y las barras).

     plt.figure(figsize=(12,8)) plt.title('Distribution of Truck Configurations') plt.xlabel('Number of Axles') plt.ylabel('Frequency [%]') ax = (dfWIM.AXLES.value_counts()/len(df)*100).sort_index().plot(kind="bar", rot=0) ax.set_yticks(np.arange(0, 110, 10)) ax2 = ax.twinx() ax2.set_yticks(np.arange(0, 110, 10)*len(df)/100) for p in ax.patches: ax.annotate('{:.2f}%'.format(p.get_height()), (p.get_x()+0.15, p.get_height()+1)) 

enter image description here

Puedes hacer esto haciendo un eje twinx para las frecuencias. Puede cambiar los dos ejes y para que las frecuencias permanezcan a la izquierda y los recuentos a la derecha, pero sin tener que volver a calcular el eje de recuentos (aquí usamos tick_left() y tick_right() para mover los ticks y set_label_position para mover el tags de ejes

A continuación, puede configurar los ticks utilizando el módulo matplotlib.ticker , específicamente ticker.MultipleLocator y ticker.LinearLocator .

En cuanto a sus anotaciones, puede obtener las ubicaciones x e y para las 4 esquinas de la barra con patch.get_bbox().get_points() . Esto, junto con la configuración correcta de la alineación horizontal y vertical, significa que no necesita agregar ningún desplazamiento arbitrario a la ubicación de la anotación.

Finalmente, debe desactivar la cuadrícula para el eje hermanado, para evitar que aparezcan líneas de cuadrícula en la parte superior de las barras ( ax2.grid(None) )

Aquí hay un script de trabajo:

 import pandas as pd import matplotlib.pyplot as plt import numpy as np import seaborn as sns import matplotlib.ticker as ticker # Some random data dfWIM = pd.DataFrame({'AXLES': np.random.normal(8, 2, 5000).astype(int)}) ncount = len(dfWIM) plt.figure(figsize=(12,8)) ax = sns.countplot(x="AXLES", data=dfWIM, order=[3,4,5,6,7,8,9,10,11,12]) plt.title('Distribution of Truck Configurations') plt.xlabel('Number of Axles') # Make twin axis ax2=ax.twinx() # Switch so count axis is on right, frequency on left ax2.yaxis.tick_left() ax.yaxis.tick_right() # Also switch the labels over ax.yaxis.set_label_position('right') ax2.yaxis.set_label_position('left') ax2.set_ylabel('Frequency [%]') for p in ax.patches: x=p.get_bbox().get_points()[:,0] y=p.get_bbox().get_points()[1,1] ax.annotate('{:.1f}%'.format(100.*y/ncount), (x.mean(), y), ha='center', va='bottom') # set the alignment of the text # Use a LinearLocator to ensure the correct number of ticks ax.yaxis.set_major_locator(ticker.LinearLocator(11)) # Fix the frequency range to 0-100 ax2.set_ylim(0,100) ax.set_ylim(0,ncount) # And use a MultipleLocator to ensure a tick spacing of 10 ax2.yaxis.set_major_locator(ticker.MultipleLocator(10)) # Need to turn the grid on ax2 off, otherwise the gridlines end up on top of the bars ax2.grid(None) plt.savefig('snscounter.pdf') 

enter image description here

Lo hice funcionar usando el matplotlib de barras del matplotlib central. No tenía sus datos obviamente, pero adaptarlos a los suyos debería ser sencillo. enter image description here

Enfoque

matplotlib el eje gemelo de matplotlib y matplotlib los datos como barras en el segundo objeto Axes . El rest es solo un poco de manipulación para corregir las marcas y hacer anotaciones.

Espero que esto ayude.

Código

 import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib from mpl_toolkits.mplot3d import Axes3D import seaborn as sns tot = np.random.rand( 1 ) * 100 data = np.random.rand( 1, 12 ) data = data / sum(data,1) * tot df = pd.DataFrame( data ) palette = sns.husl_palette(9, s=0.7 ) ### Left Axis # Plot nothing here, autmatically scales to second axis. fig, ax1 = plt.subplots() ax1.set_ylim( [0,100] ) # Remove grid lines. ax1.grid( False ) # Set ticks and add percentage sign. ax1.yaxis.set_ticks( np.arange(0,101,10) ) fmt = '%.0f%%' yticks = matplotlib.ticker.FormatStrFormatter( fmt ) ax1.yaxis.set_major_formatter( yticks ) ### Right Axis # Plot data as bars. x = np.arange(0,9,1) ax2 = ax1.twinx() rects = ax2.bar( x-0.4, np.asarray(df.loc[0,3:]), width=0.8 ) # Set ticks on x-axis and remove grid lines. ax2.set_xlim( [-0.5,8.5] ) ax2.xaxis.set_ticks( x ) ax2.xaxis.grid( False ) # Set ticks on y-axis in 10% steps. ax2.set_ylim( [0,tot] ) ax2.yaxis.set_ticks( np.linspace( 0, tot, 11 ) ) # Add labels and change colors. for i,r in enumerate(rects): h = r.get_height() r.set_color( palette[ i % len(palette) ] ) ax2.text( r.get_x() + r.get_width()/2.0, \ h + 0.01*tot, \ r'%d%%'%int(100*h/tot), ha = 'center' ) 

Creo que primero puedes establecer los tics principales de forma manual y luego modificar cada etiqueta

 dfWIM = pd.DataFrame({'AXLES': np.random.randint(3, 10, 1000)}) total = len(dfWIM)*1. plt.figure(figsize=(12,8)) ax = sns.countplot(x="AXLES", data=dfWIM, order=[3,4,5,6,7,8,9,10,11,12]) plt.title('Distribution of Truck Configurations') plt.xlabel('Number of Axles') plt.ylabel('Frequency [%]') for p in ax.patches: ax.annotate('{:.1f}%'.format(100*p.get_height()/total), (p.get_x()+0.1, p.get_height()+5)) #put 11 ticks (therefore 10 steps), from 0 to the total number of rows in the dataframe ax.yaxis.set_ticks(np.linspace(0, total, 11)) #adjust the ticklabel to the desired format, without changing the position of the ticks. _ = ax.set_yticklabels(map('{:.1f}%'.format, 100*ax.yaxis.get_majorticklocs()/total)) 

enter image description here

    Intereting Posts