pandas 海运:直方图不从0开始

rpppsulh  于 2022-11-20  发布在  其他
关注(0)|答案(1)|浏览(216)

我试图用以下代码在SeaBorn中绘制不同的直方图:

ax = sns.displot(data=municipios,
            x="DENSIDAD_HABITACIONAL",
            multiple="stack",
            height=6,
            aspect=2,
            kind='hist', 
            bins=AGRUPAMIENTO, 
            stat='density',
            kde=True).set(title='Histograma de Densidad Habitacional')

ax.set(xlabel='Densidad Habitacional (hab/km´2)', ylabel='Cantidad relativa de municipios')

plt.axvline(x=municipios.DENSIDAD_HABITACIONAL.mean(),
            color='red')
plt.axvline(x=municipios.DENSIDAD_HABITACIONAL.median(),
            color='green')
plt.axvline(x=moda(municipios.DENSIDAD_HABITACIONAL,AGRUPAMIENTO),
            color='blue')

plt.legend(handles=etiquetas)

ax = sns.displot(data=municipios,
            x="PORCENTAJE_NBI",
            multiple="stack",
            height=6,
            aspect=2,
            kind='hist', 
            bins=AGRUPAMIENTO, 
            stat='density',
            ax=0,
            kde=True).set(title='Histograma de %NBI')

ax.set(xlabel='%NBI', ylabel='Cantidad relativa de municipios')

plt.axvline(x=municipios.PORCENTAJE_NBI.mean(),
            color='red')
plt.axvline(x=municipios.PORCENTAJE_NBI.median(),
            color='green')
plt.axvline(x=moda(municipios.PORCENTAJE_NBI,AGRUPAMIENTO),
            color='blue')

我的结果是这样的:

因此,在屏幕截图中显示的第一个直方图中,第一个条形(位于所有条形的左侧)从范围0开始。但是,在第二个直方图中,我们可以看到第一个条形并不是从0开始,而是从一个更大的值开始。
此外,我还想在绘制的轴中显示分隔每个条形的值,也就是说,分隔区间的分隔符,而不是默认情况下自动显示的分隔符(我的意思是,例如,在第二个直方图中,[0.0,2.29,4.57,...]而不是[0,5,10,...])。
我定义了一个函数,它返回一个包含每个区间的频率的序列,以及一个包含这些分隔符的序列,如下所示:
频率:[5.0、60.0、30.0、17.0、10.0、11.0、3.0、2.0、2.0、0.0、1.0]
分隔符号:[0.0、2.29、4.57、6.86、9.15、11.44、13.72、16.01、18.3、20.59、22.87、25.16]
通过这种方式,我可以在分析形式中看到这些值,而不是在图形形式中。这些是与所示的第二个直方图(NBI百分比)相对应的值。
我还使用该函数返回的值来计算另一个已定义函数中变量的众数。如果有人感兴趣,我可以共享这两个函数的代码(计算间隔的函数和计算众数的函数)。
最后,通过查看这些值,我还可以发现存在一些问题:在第二个间隔中,我们有60个观测值,在第三个间隔中,我们有30个观测值。但是,在直方图中,我们可以看到第三个柱的高度低于第二个柱的一半,因为30正好是60的一半。
有人知道我该怎么做吗?
多谢了!

ruarlubt

ruarlubt1#

@约翰C
我试着将一个序列传递给“displot”的“bins”属性,并很好地解决了直方图从零开始的问题,以及列高的正确比例问题。因此,我的代码中提到的函数,以及bin间隔的常量如下(西班牙语注解):
区间常数:

#En la constante 'AGRUPAMIENTO', definimos la cantidad de intervalos en los que subdividiremos
#la población estadística (nos referimos a la cantidad de registros, no a la cantidad de habitantes),
#para realizar el análisis correspondiente.
#Se da la casualidad de que, convenientemente, tenemos un total de 141 registros en nuestra
#base de datos, siendo dicha cantidad un cuadrado perfecto de 12, por lo que este último valor
#nos resulta ideal para realizar dicho agrupamiento.
#Como cuestión importante, este valor determina la cantidad de delimitaciones de los intervalos,
#por lo cual la cantidad de intervalos corresponderá a esta cantidad menos 1.
#Esto es, si tenemos 12 delimitadores, entonces tendremos 11 intervalos.
AGRUPAMIENTO=12

间隔函数:

#Esta función recibe como parámetro en 'columna' una columna de N observaciones, y
#en 'bins' la cantidad de delimitaciones de intervalos de clase, en las que categorizamos dichas observaciones.
#Devuelve tanto una lista de intervalos, con su frecuencia correspondiente en cada uno de sus elementos,
#como un array de divisiones, el cual define los límites de cada intervalo
def intervalos(columna,bins):
    #En cant_reg me guardo la cantidad de registros de la columna
    cant_reg=columna.count()
    #En max_valor obtengo el máximo valor del elemento de dicha columna
    max_valor=columna.max()
    
    #Me genero un vector de divisiones, con una cantidad de elementos igual
    #a la cantidad de intervalos de clase mas 1, cuyos valores
    #irán desde 0 hasta el máximo valor del conjunto,
    #subdivididos linealmente
    divisiones=np.linspace(0,max_valor,bins).tolist()

    #Me genero un array con tantos elementos como intervalos de clase,
    #inicialmente cargado con ceros
    array_intervalos=np.zeros(bins-1)
    
    #Recorro con un for, del cual necesito los índices, comenzado desde 1, el vector de divisiones
    #con la variable i, de forma que puedo ir recorriendo los intervalos de clase
    for i in range(1,bins):
        #Declaro una variable, que se reiniciará a 0 en cada iteración
        #del for de primer orden, donde contaré las muestras de la
        #columna que se ubican dentro de cada rango
        cont_muestras=0
        
        #Con otro for anidado, recorro cada elemento de la columna
        for elemento in columna:
            
            #Si el elemento en el que estoy parado ahora, se encuentra
            #en el intervalo de clase que estoy recorriendo
            if elemento > divisiones[i-1] and elemento <= divisiones[i]:
                #Incremento el contador de muestras
                cont_muestras=cont_muestras+1
        
        #Una vez ejecutado el for anidado, cargo en la posicion i-1
        #de mi array de intervalos de clase, la cantidad de muestras
        #que conté en el rango correspondiente
        array_intervalos[i-1]=cont_muestras
        
    #Una vez ejecutados los for anidaos, tengo el array de intervalos
    #cargado con los valores correspondientes
    
    #Lo convierto en una lista
    lista_intervalos=array_intervalos.tolist()
    
    #Devuelvo una tupla con la lista de intervalos y las divisiones
    return (lista_intervalos, divisiones)

模式功能:

#Esta función recibe como parámetro en 'columna' una columna de N observaciones, y
#en 'bins' la cantidad de delimitaciones de intervalos de clase, en las que categorizamos dichas observaciones.
#Devuelve la moda aproximada calculada para dicha columna, de acuerdo
#a la cantidad de intervalos de clase (el valor de 'bins' - 1) en los que agrupamos la muestra
def moda(columna,bins):
    
    #A partir de la función 'intervalos', a la que le paso los parámetros
    #recibidos, obtengo las secuencias con los valores que necesito para calcular la moda   
    lista_intervalos,divisiones= intervalos(columna,bins)
    
    #De la lista de intervalos, me guardo el índice correspondiente
    #a su valor máximo. Esta será la posición de nuestro intervalo modal (donde se encuentra la moda)
    ind_max_val = lista_intervalos.index(max(lista_intervalos))
     
    
    #El límite inferior del intervalo modal será el elemento del array
    #de subdivisiones cuyo índice sea igual al del mayor elemento de la
    #lista de intervalos
    
    limite_inferior=divisiones[ind_max_val]
    
    #Los valores 'a' y 'b' representan:
    #a: Diferencia entre la altura del intervalo modal, y la del intervalo anterior
    #b: Diferencia entre la altura del intervalo modal, y la del intervalo posterior
    #Como se calcularan en base a la posicion del intervalo modal, dependiendo de si el
    #mismo está situado en un extremo o en el centro de la lsita,necesitamos tenerlos por
    #fuera del scope del if, por lo que los declaramos aqui con valor 0
    a=0
    b=0
    
    #El valor de c, nos dará siempre la amplitud del intervalo modal (como de cualquier otro)
    c=divisiones[ind_max_val+1] - divisiones[ind_max_val]
    
    #Calculamos el valor de a, dependiendo de si el intervalo modal
    #es el que está a la izquierda de todos, o no
    if ind_max_val > 0:
        a=lista_intervalos[ind_max_val] - lista_intervalos[ind_max_val-1]
    else:
        a=lista_intervalos[ind_max_val]
    
    #Calculamos el valor de b, dependiendo de si el intervalo modal
    #es el que está a la derecha de todos, o no
    if ind_max_val < (bins-1):
        b=lista_intervalos[ind_max_val] - lista_intervalos[ind_max_val+1]
    else:
        b=lista_intervalos[ind_max_val]     
    
    #Una vez obtenidos todos los valores, los aplicamos en la formula de la moda
    #y el resultado lo guardamos en una variable
    
    moda= limite_inferior + (a/(a+b))*c
    
    #Finalmente, devolvemos el valor de la moda calculado
    return moda

一旦我们定义了这些函数,我们就可以正确地绘制直方图:

#Asignamos colores diferentes a la media,mediana y moda para la leyenda de los gráficos
ref_media = mpatches.Patch(color='red', label='Media')
ref_mediana = mpatches.Patch(color='green', label='Mediana')
ref_moda = mpatches.Patch(color='blue', label='Moda')
etiquetas=[ref_media,ref_mediana,ref_moda]

...

ax = sns.displot(data=municipios,
            x="PORCENTAJE_NBI",
            multiple="stack",
            height=6,
            aspect=2,
            kind='hist', 
            bins=intervalos(municipios.PORCENTAJE_NBI,AGRUPAMIENTO)[1], #Instead of bins=AGRUPAMIENTO,
            stat='density',
            kde=True).set(title='Histograma de %NBI')

ax.set(xlabel='%NBI', ylabel='Cantidad relativa de municipios')

plt.axvline(x=municipios.PORCENTAJE_NBI.mean(),
            color='red')
plt.axvline(x=municipios.PORCENTAJE_NBI.median(),
            color='green')
plt.axvline(x=moda(municipios.PORCENTAJE_NBI,AGRUPAMIENTO),
            color='blue')

plt.legend(handles=etiquetas)

那么,我的结果是:

现在,我们可以看到,通过函数计算的模式值与用于查找它的图形方法完全一致。
所以,我已经解决了最重要的问题,但没有解决坐标轴中显示的分隔符之一。我试着用'histplot'代替'displot',看看我是否能解决第二个问题,但我仍然不能。
下面是我的历史图代码:

df = pd.DataFrame({'nbi': municipios.PORCENTAJE_NBI})
p1 = sns.histplot(data=df, x='nbi', bins=intervalos(municipios.PORCENTAJE_NBI,AGRUPAMIENTO)[1], stat='density',kde=True)
plt.show()

而我的结果是:

作为详细信息,以下是每个度量的值:
平均值:6.468369
中位数:500万
众数:3.767273

相关问题