Python实现峰峦图

文摘   2024-11-02 10:01   北京  

峰峦图

  峰峦图是一种展示数据核密度的图形方法,由于各种数据前后重叠像一重重山川而得名。峰峦图可以通过seaborn、joypy实现,也可以通过matplotlib实现。下面简单介绍这三种种峰峦图的统计方法。

matplotlib实现峰峦图

  峰峦图实际上是一种统计图,不展示原始数据。所以我们需要先对数据进行统计。下面是虚构的一个站点的全年逐日平均数据:

  假设我们试图展示逐月的日平均气温核密度,应该首先将这些数据按照月份分组(groupby),然后将平均气温分箱后(cut),统计各气温区间的分布数量(value_counts)。
  首先是将时间列作为分组依据,调用dt属性后,使用month分组,这样我们将得到1-12月份的分组:

df.groupby(df['时间'].dt.month).groups.keys()

out_put:→dict_keys([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
  我们可以使用for循环对每个分组操作,但pandas向我们提供了一个广播函数到所有分组的方法apply,所以我们到时候使用这个方法。
  我们需要定义一个广播函数,我们将他命名为stats_temp:

def stats_temp(dataframe):
    #对输入的DataFrame分箱:
    cut_df=pd.cut(dataframe['平均气温'],bins=np.arange(-5,31,1))
    #统计各箱体气温数量
    stats_df=cut_df.value_counts(sort=False)
    return stats_df

  对所有分组使用该函数后,我们将得到各月份的,各气温阶段的落区统计(非常有用的技巧,其他时间序列也可以通过这种方法快速统计,定义一个不错的函数在各分组apply,比for循环快还方便):

df.groupby(df['时间'].dt.month).apply(stats_temp)

  由于有十二个月,相当于要生成十二个ax,fig.add_axes或者subplot一个个添加太慢了,所以我们选择subplots:

fig,axes=plt.subplots(12,1)

  再进行方方面面的美化修饰参数,并调节每张子图的垂直距离为负数,然后在每张ax上逐月绘制图像,通过plot和fill_between绘制峰峦。在循环中解除每张子图的axis,这样每张图像都不会产生遮挡。在运行到最后一张子图时,添加横坐标的气温坐标:

plt.rcParams['font.sans-serif']=['FangSong']
colors=mpl.colormaps['Spectral'].resampled(12)(range(12))
fig,axes=plt.subplots(12,1,**{'figsize':(5,3),'dpi':500})
fig.subplots_adjust(hspace=-0.7)
fig.suptitle(x=0.5,y=0.9,t='各月气温核密度分布',fontsize=10)
for i,ax in enumerate(axes.flatten()):
    ax.text(-2,0.5,'{}月'.format(i+1),fontsize=8)
    ax.plot(range(len(df_counts.iloc[i].values)),df_counts.iloc[i].values,lw=0.5,c=colors[i])
    ax.fill_between(range(len(df_counts.iloc[i].values)),
                    df_counts.iloc[i].values,
                    y2=0,
                    where=(df_counts.iloc[i].values>0),
                    interpolate=True,color=colors[i])
    ax.axis('off')
    if i==11:
        ax.set_xticks(range(len(df_counts.iloc[i].values))[::4])
        ax.set_xticklabels(np.arange(-5,31,1)[1::4])
        ax.axis('on')
        ax.spines[:].set_visible(False)
        ax.set_facecolor('none')
        ax.tick_params(labelsize=6,width=0.5)
        ax.yaxis.set_major_locator(mticker.NullLocator())
        ax.set_xlabel('气温',fontsize=6)

  这里由于给出的cut分箱较大,同时样本量较少,锯齿较严重,但原理是不变的。也可以使用专门的核密度估计函数。这里是展示峰峦图的绘图原理。掌握了原理是不怕数据变的。

seaborn实现峰峦图

  seaborn本来就是matplotlib的高级封装,提供了专门的命令来绘制图像,同时官网也有demo。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="white", rc={"axes.facecolor": (0000)})

# Create the data
rs = np.random.RandomState(1979)
x = rs.randn(500)
g = np.tile(list("ABCDEFGHIJ"), 50)
df = pd.DataFrame(dict(x=x, g=g))
m = df.g.map(ord)
df["x"] += m

# Initialize the FacetGrid object
pal = sns.cubehelix_palette(10, rot=-.25, light=.7)
g = sns.FacetGrid(df, row="g", hue="g", aspect=15, height=.5, palette=pal)

# Draw the densities in a few steps
g.map(sns.kdeplot, "x",
      bw_adjust=.5, clip_on=False,
      fill=True, alpha=1, linewidth=1.5)
g.map(sns.kdeplot, "x", clip_on=False, color="w", lw=2, bw_adjust=.5)

# passing color=None to refline() uses the hue mapping
g.refline(y=0, linewidth=2, linestyle="-", color=None, clip_on=False)


# Define and use a simple function to label the plot in axes coordinates
def label(x, color, label):
    ax = plt.gca()
    ax.text(0.2, label, fontweight="bold", color=color,
            ha="left", va="center", transform=ax.transAxes)


g.map(label, "x")

# 这一步其实也就是我们调节每个子图的命令,不难看出seaborn和matplotlib思路是一样的
g.figure.subplots_adjust(hspace=-.25)

# Remove axes details that don't play well with overlap
g.set_titles("")
g.set(yticks=[], ylabel="")
g.despine(bottom=True, left=True)

joypy实现峰峦图

  joypy是专门绘制峰峦图的库。joypy自带核密度估计,我们只需要给出分组即可,不用另外进行统计。

import joypy
fig,axes=joypy.joyplot(df.groupby(df['时间'].dt.month))


气python风雨
主要发一些涉及大气科学的Python文章与个人学习备忘录
 最新文章