Saltar al contenido

Histograma circular / polar en python

Ya no busques más por otras páginas ya que has llegado al lugar necesario, contamos con la respuesta que deseas y sin complicaciones.

Solución:

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

ingrese 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 ayudarlo a comenzar.

En general, navegar por la galería de matplotlib suele ser un buen punto de partida.

Aquí, usé el bottom palabra clave para dejar el centro vacío, porque creo que vi una pregunta anterior suya 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 muestra arriba, solo use bottom=0 (o déjalo fuera ya que 0 es el predeterminado).

Respuesta rápida

Usa la función circular_hist() Escribí a continuación.

Por defecto, esta función traza la frecuencia proporcional a zona, no radio (el razonamiento detrás de esta decisión se ofrece a continuación en “respuesta de forma más larga”).

def circular_hist(ax, x, bins=16, density=True, offset=0, gaps=True):
    """
    Produce a circular histogram of angles on ax.

    Parameters
    ----------
    ax : matplotlib.axes._subplots.PolarAxesSubplot
        axis instance created with subplot_kw=dict(projection='polar').

    x : array
        Angles to plot, expected in units of radians.

    bins : int, optional
        Defines the number of equal-width bins in the range. The default is 16.

    density : bool, optional
        If True plot frequency proportional to area. If False plot frequency
        proportional to radius. The default is True.

    offset : float, optional
        Sets the offset for the location of the 0 direction in units of
        radians. The default is 0.

    gaps : bool, optional
        Whether to allow gaps between bins. When gaps = False the bins are
        forced to partition the entire [-pi, pi] range. The default is True.

    Returns
    -------
    n : array or list of arrays
        The number of values in each bin.

    bins : array
        The edges of the bins.

    patches : `.BarContainer` or list of a single `.Polygon`
        Container of individual artists used to create the histogram
        or list of such containers if there are multiple input datasets.
    """
    # Wrap angles to [-pi, pi)
    x = (x+np.pi) % (2*np.pi) - np.pi

    # Force bins to partition entire circle
    if not gaps:
        bins = np.linspace(-np.pi, np.pi, num=bins+1)

    # Bin data and record counts
    n, bins = np.histogram(x, bins=bins)

    # Compute width of each bin
    widths = np.diff(bins)

    # By default plot frequency proportional to area
    if density:
        # Area to assign each bin
        area = n / x.size
        # Calculate corresponding bin radius
        radius = (area/np.pi) ** .5
    # Otherwise plot frequency proportional to radius
    else:
        radius = n

    # Plot data on ax
    patches = ax.bar(bins[:-1], radius, zorder=1, align='edge', width=widths,
                     edgecolor='C0', fill=False, linewidth=1)

    # Set the direction of the zero angle
    ax.set_theta_offset(offset)

    # Remove ylabels for area plots (they are mostly obstructive)
    if density:
        ax.set_yticks([])

    return n, bins, patches

Uso de ejemplo:

import matplotlib.pyplot as plt
import numpy as np

angles0 = np.random.normal(loc=0, scale=1, size=10000)
angles1 = np.random.uniform(0, 2*np.pi, size=1000)

# Construct figure and axis to plot on
fig, ax = plt.subplots(1, 2, subplot_kw=dict(projection='polar'))

# Visualise by area of bins
circular_hist(ax[0], angles0)
# Visualise by radius of bins
circular_hist(ax[1], angles1, offset=np.pi/2, density=False)

imágenes de ejemplo

Respuesta de forma más larga

Siempre recomendaría precaución al usar histogramas circulares, ya que pueden confundir fácilmente a los lectores.

En particular, le aconsejo que se mantenga alejado de los histogramas circulares donde frecuencia y radio se trazan proporcionalmente. Recomiendo esto porque la mente se ve muy afectada por la zona de los contenedores, no solo por su extensión radial. Esto es similar a cómo estamos acostumbrados a interpretar gráficos circulares: por zona.

Entonces, en lugar de usar el radial extensión de un contenedor para visualizar la cantidad de puntos de datos que contiene, recomiendo visualizar la cantidad de puntos por área.

El problema

Considere las consecuencias de duplicar el número de puntos de datos en un intervalo de histograma dado. En un histograma circular donde la frecuencia y el radio son proporcionales, el radio de este intervalo aumentará en un factor de 2 (ya que el número de puntos se ha duplicado). Sin embargo, ¡el área de este contenedor se habrá incrementado en un factor de 4! Esto se debe a que el área del contenedor es proporcional al radio al cuadrado.

Si esto todavía no parece un gran problema, veámoslo gráficamente:

histogramas de frecuencia

Ambos gráficos anteriores visualizan los mismos puntos de datos.

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

Sin embargo, observe el gráfico de la derecha (frecuencia proporcional al radio). A primera vista, su mente se ve muy afectada por el área de los contenedores. Serías perdonado por pensar que hay mas que el doble de puntos en el bin (0, pi / 4) que en el bin (-pi / 4, 0). Sin embargo, te habrías engañado. Es solo en una inspección más cercana del gráfico (y del eje radial) que se da cuenta de que hay exactamente el doble de puntos de datos en el bin (0, pi / 4) que en el bin (-pi / 4, 0). No más del doble, como sugirió originalmente el gráfico.

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

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn')

# Generate data with twice as many points in (0, np.pi/4) than (-np.pi/4, 0)
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")

# Plot "standard" histogram
ax.hist(angles, bins=bins)
# Fiddle with labels and limits
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$'])

# bin data for our polar histogram
count, bin = np.histogram(angles, bins=bins)
# Plot polar histogram
polar_ax.bar(bin[:-1], count, align='edge', color='C0')

# Fiddle with labels and limits
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)

Una solución

Dado que estamos tan afectados por la zona de los bins en histogramas circulares, encuentro más efectivo asegurar que el área de cada bin sea proporcional al número de observaciones en él, en lugar del radio. Esto es similar a cómo estamos acostumbrados a interpretar gráficos circulares, donde el área es la cantidad de interés.

Usemos el conjunto de datos que usamos en el ejemplo anterior para reproducir los gráficos basados ​​en el área, en lugar del radio:

histogramas de densidad

Creo que los lectores tienen menos posibilidades de ser engañado a primera vista de este gráfico.

Sin embargo, al trazar un histograma circular con un área proporcional al radio, tenemos la desventaja de que nunca habrías sabido que hay exactamente el doble de puntos en el contenedor (0, pi / 4) que en el contenedor (-pi / 4, 0) con solo observar las áreas. Aunque, puede contrarrestar esto anotando cada contenedor con su densidad correspondiente. Creo que esta desventaja es preferible a engañar al lector.

Por supuesto, me aseguraría de que se colocara una leyenda informativa junto a esta figura para explicar que aquí visualizamos la frecuencia con el área, no con el radio.

Los gráficos anteriores se crearon como:

fig = plt.figure()
ax = fig.add_subplot(1, 2, 1)
polar_ax = fig.add_subplot(1, 2, 2, projection="polar")

# Plot "standard" histogram
ax.hist(angles, bins=bins, density=True)
# Fiddle with labels and limits
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$'])

# bin data for our polar histogram
counts, bin = np.histogram(angles, bins=bins)
# Normalise counts to compute areas
area = counts / angles.size
# Compute corresponding radii from areas
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$'])

Reseñas y calificaciones de la guía

Si haces scroll puedes encontrar las interpretaciones de otros creadores, tú además puedes mostrar el tuyo si lo crees conveniente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *