
01
02
03
04
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import gaussian_kdefrom mpl_toolkits.mplot3d import Axes3D
file_path = r"G:\Maping\demo\agri_green_kde_sample.csv"df = pd.read_csv(file_path)plt.rcParams["font.family"] = "Times New Roman"plt.rcParams["axes.unicode_minus"] = False
df["year"] = df["year"].astype(int)df["region"] = df["region"].astype(str)df["sample_id"] = df["sample_id"].astype(str)df["value"] = df["value"].astype(float)sample_n_per_group = 10if sample_n_per_group isnotNone:df = (df.groupby(["region", "year"], group_keys=False).apply(lambda x: x.sample(n=min(sample_n_per_group, len(x)),random_state=42)).reset_index(drop=True))regions = sorted(df["region"].unique())years = sorted(df["year"].unique())
(a)~(h)。nrows, ncols = 2, 4fig = plt.figure(figsize=(24, 10))panel_labels = list("abcdefgh")
x_min = df["value"].min() - 0.03x_max = df["value"].max() + 0.03x_grid = np.linspace(x_min, x_max, 120)
global_zmax = 0kde_dict = {}for region in regions:Z_list = []for year in years:vals = df.loc[(df["region"] == region) & (df["year"] == year),"value"].dropna().valuesiflen(vals) < 2or np.std(vals) == 0:z = np.zeros_like(x_grid)else:kde = gaussian_kde(vals, bw_method=0.35)z = kde(x_grid)Z_list.append(z)global_zmax = max(global_zmax, z.max())kde_dict[region] = np.array(Z_list)
for i, region inenumerate(regions[:8], start=1):ax = fig.add_subplot(nrows, ncols, i, projection="3d")Z = kde_dict[region]X = np.tile(x_grid, (len(years), 1))Y = np.tile(np.array(years).reshape(-1, 1), (1, len(x_grid)))surf = ax.plot_surface(X, Y, Z,cmap="viridis",edgecolor="white",linewidth=0.25,antialiased=True,alpha=0.95,rstride=1,cstride=1)for j, year inenumerate(years):ax.plot(x_grid,np.full_like(x_grid, year),Z[j, :],color="black",linewidth=0.45,alpha=0.6)ax.set_title(f"({panel_labels[i-1]})", fontsize=14, pad=8)ax.set_xlabel("Agricultural Green Development", fontsize=10, labelpad=8)ax.set_ylabel("Year", fontsize=10, labelpad=8)ax.set_zlabel("")ax.text2D(-0.10, 0.52, "Kernel Density",transform=ax.transAxes,rotation=90,va="center",ha="center",fontsize=10)ax.set_xlim(x_min, x_max)ax.set_ylim(min(years), max(years))ax.set_zlim(0, global_zmax * 1.05)ax.set_yticks(years[::2] iflen(years) > 6else years)ax.tick_params(axis="both", which="major", labelsize=8)ax.tick_params(axis="z", which="major", labelsize=8)ax.view_init(elev=24, azim=-125)ax.xaxis.pane.fill = Falseax.yaxis.pane.fill = Falseax.zaxis.pane.fill = Falseax.grid(True, linestyle="--", alpha=0.2)
05
06