Histogtwig circular para Python

Tengo datos periódicos y la distribución se visualiza mejor alrededor de un círculo. Ahora la pregunta es ¿cómo puedo hacer esta visualización usando matplotlib? Si no, ¿se puede hacer fácilmente en Python?

Mi código aquí demostrará una aproximación cruda de la distribución alrededor de un círculo:

from matplotlib import pyplot as plt import numpy as np #generatin random data a=np.random.uniform(low=0,high=2*np.pi,size=50) #real circle b=np.linspace(0,2*np.pi,1000) a=sorted(a) plt.plot(np.sin(a)*0.5,np.cos(a)*0.5) plt.plot(np.sin(b),np.cos(b)) plt.show() 

introduzca la descripción de la imagen aquí

Hay algunos ejemplos en una pregunta sobre SX para Mathematica : introduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquí

Partiendo de este ejemplo de la galería, puedes hacer

introduzca la descripción de la imagen aquí

 import numpy as np import matplotlib.pyplot as plt N = 80 bottom = 8 max_height = 4 theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False) radii = max_height*np.random.rand(N) width = (2*np.pi) / N ax = plt.subplot(111, polar=True) bars = ax.bar(theta, radii, width=width, bottom=bottom) # Use custom colors and opacity for r, bar in zip(radii, bars): bar.set_facecolor(plt.cm.jet(r / 10.)) bar.set_alpha(0.8) plt.show() 

Por supuesto, hay muchas variaciones y ajustes, pero esto debería ayudarte a comenzar.

En general, navegar por la galería matplotlib suele ser un buen lugar para comenzar.

Aquí, utilicé la palabra clave de bottom para dejar el centro vacío, porque creo que vi una pregunta anterior con un gráfico más parecido al que tengo, así que supongo que eso es lo que quiere. Para obtener las cuñas completas que se muestran arriba, solo use bottom=0 (o dejarlo fuera ya que 0 es el valor predeterminado).

Estoy 5 años tarde al juego, pero …

Los histogtwigs circulares pueden engañar muy fácilmente a los lectores. Como tal, siempre recomendaría precaución al usarlos.

En particular, aconsejaría que se mantenga alejado de los histogtwigs circulares / polares que trazan la frecuencia radialmente. Esto se debe a que la mente se ve afectada por el área de los contenedores, así como su extensión radial. En lugar de visualizar el número de puntos en un contenedor por radio, aconsejo visualizar por área.

El problema

Considere las consecuencias de duplicar el número de puntos de datos en un contenedor dado. En un histogtwig de frecuencia circular, el radio de este contenedor boostá en un factor de 2, sin embargo, el área de este contenedor boostá en un factor de 4. Esto se debe a que el área del contenedor es proporcional al radio al cuadrado, y aquí Tenemos la oportunidad de ser engañados.

Si esto no suena como un gran problema, veamos gráficamente:

introduzca la descripción de la imagen aquí

Las dos gráficas anteriores visualizan el mismo conjunto de datos.

En el gráfico de la izquierda es fácil ver que hay dos puntos de datos en el contenedor (0, pi / 4) que en el contenedor (-pi / 4, 0).

Sin embargo, eche un vistazo a la ttwig de la mano derecha. A primera vista, su mente se ve muy afectada por el área de los contenedores. Se le perdonará por pensar que hay más del doble de puntos en el contenedor (0, pi / 4) que en el contenedor (-pi / 4, 0). Sin embargo, habrías sido engañado. Es solo en una inspección más cercana del gráfico que se da cuenta de que hay exactamente el doble de puntos de datos en el contenedor (0, pi / 4) que en el contenedor (-pi / 4, 0). No más del doble , como el gráfico puede haber sugerido originalmente.

Los gráficos anteriores se pueden recrear con el siguiente código:

 import numpy as np import matplotlib.pyplot as plt angles = np.hstack([np.random.uniform(0, np.pi/4, size=100), np.random.uniform(-np.pi/4, 0, size=50)]) bins = 2 fig = plt.figure() ax = fig.add_subplot(1, 2, 1) polar_ax = fig.add_subplot(1, 2, 2, projection="polar") ax.hist(angles, bins=bins) ax.set_xlim([-np.pi/4, np.pi/4]) ax.set_xticks() ax.set_xticklabels([r'$-\pi/4$', r'$0$', r'$\pi/4$']) count, bin = np.histogram(angles, bins=2) polar_ax.bar(bin[:-1], count, align='edge', color='C0') polar_ax.set_xticks([0, np.pi/4, 2*np.pi - np.pi/4]) polar_ax.set_xticklabels([r'$0$', r'$\pi/4$', r'$-\pi/4$']) polar_ax.set_rlabel_position(90) fig.tight_layout() 

Una solución

Debido a que el área de los contenedores en los histogtwigs circulares nos afecta mucho, me parece más efectivo asegurar que el área de cada grupo sea proporcional al número de observaciones en cada grupo, en lugar del radio. Esto es similar a cómo estamos acostumbrados a interpretar gráficos circulares, donde el área es importante.

Usemos el conjunto de datos que usamos anteriormente para reproducir los gráficos según el área, en lugar del radio:

introduzca la descripción de la imagen aquí

Personalmente, encuentro este histogtwig polar más intuitivo y la hipótesis de que los lectores tienen menos posibilidades de ser engañados a primera vista. Por supuesto, me aseguraría de que se colocara un título informativo junto a la figura para explicar que aquí visualizamos contenedores con área, no con radio.

Estas plots fueron creadas como:

 fig = plt.figure() ax = fig.add_subplot(1, 2, 1) polar_ax = fig.add_subplot(1, 2, 2, projection="polar") ax.hist(angles, bins=bins, density=True) ax.set_xlim([-np.pi/4, np.pi/4]) ax.set_xticks([-np.pi/4, 0, np.pi/4]) ax.set_xticklabels([r'$-\pi/4$', r'$0$', r'$\pi/4$']) counts, bin = np.histogram(angles, bins=2) area = counts / angles.size radius = (area / np.pi)**.5 polar_ax.bar(bin[:-1], radius, align='edge', color='C0') # Label angles according to convention polar_ax.set_xticks([0, np.pi/4, 2*np.pi - np.pi/4]) polar_ax.set_xticklabels([r'$0$', r'$\pi/4$', r'$-\pi/4$']) fig.tight_layout() 

Poniendolo todo junto

Si crea muchos histogtwigs circulares, es mejor crear una función de trazado que pueda reutilizar fácilmente. A continuación incluyo una función que escribí y uso en mi trabajo:

 def rose_plot(ax, angles, bins=16, density=None, xticks=True, **param_dict): """ Plots polar histogram of angles. ax must have been created with using kwarg subplot_kw=dict(projection='polar'). """ # To be safe, make a coppy of angles before wrapping data = angles.copy() # Wrap angles to range [0, 2pi) data %= 2*np.pi # Remove distracting grid ax.grid(False) # Bin data and record counts count, bin = np.histogram(data, bins=np.linspace(0, 2*np.pi, num=bins+1)) # By default plot density (frequency potentially misleading) if density is None or density is True: # Area to assign each bin area = count / data.size # Calculate corresponding bin radius radius = (area / np.pi)**.5 else: radius = count # Plot data on ax ax.bar(bin[:-1] + np.pi/bins, radius, width=2*np.pi/bins, zorder=1, edgecolor='C0', fill=False, linewidth=1, **param_dict) # Remove ylabels, they are obstructive and not informative ax.set_yticks([]) if xticks: # Label angles according to convention angle_pos = [0, np.pi/2, np.pi, 3*np.pi/2] angle_label = ['$0$', r'$\pi/2$', r'$-\pi, \pi$', r'-$\pi/2$'] ax.set_xticks(angle_pos) ax.set_xticklabels(angle_label) else: ax.set_xticks([]) 

Es super facil de usar esta funcion Aquí demuestro su uso para algunas direcciones generadas al azar:

 angles0 = np.random.normal(loc=0, scale=1, size=10000) angles1 = np.random.uniform(-np.pi, np.pi, size=100) # Visualise with polar histogram fig, ax = plt.subplots(1, 2, subplot_kw=dict(projection='polar')) rose_plot(ax[0], angles0) rose_plot(ax[1], angles1) fig.tight_layout() 

introduzca la descripción de la imagen aquí

Una nota final sobre la convención.

Las direcciones se pueden representar como rotaciones con respecto a alguna dirección cero, u origen. El practicante es libre de elegir la dirección cero cuando lo considere apropiado. De manera similar, el profesional puede elegir si se toma una rotación hacia la derecha o hacia la izquierda como dirección positiva.

Arriba, tomé el ángulo cero como la dirección de (0,0) ya lo largo del eje x positivo, y las rotaciones en sentido antihorario como la dirección positiva. Me gustan mis angularjs en radianes y restringido al rango (-pi, pi), sin embargo, puede que no.

Al cambiar un par de líneas en la función anterior, sin embargo, puede trazar cualquier convención que desee:

introduzca la descripción de la imagen aquí