python pic3:折线柱状

折线柱状

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D

# ===== 强制中文字体设置 =====
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# ========== 1. 读取Excel数据 ==========
file_path = r"D:/趋势图.xlsx"
df = pd.read_excel(file_path, engine='openpyxl')

def extract_interval_value(text):
    try:
        parts = str(text).split('-')
        for p in reversed(parts):
            if p != '':
                return float(p)
        return np.nan
    except:
        return np.nan

interval_raw = df["区间"].values
area_before = df["作业前面积"].values
area_after = df["作业后面积"].values
pct_before = df["作业前面积占比"].values
pct_after = df["作业后面积占比"].values

intervals = np.array([extract_interval_value(v) for v in interval_raw])
valid_mask = ~(np.isnan(intervals) | np.isnan(area_before) | np.isnan(area_after) |
               np.isnan(pct_before) | np.isnan(pct_after))
intervals = intervals[valid_mask]
area_before = area_before[valid_mask]
area_after = area_after[valid_mask]
pct_before = pct_before[valid_mask]
pct_after = pct_after[valid_mask]

print("有效数据行数:", len(intervals))

# ========== 2. 3D画布(白底)==========
fig = plt.figure(figsize=(18, 12), dpi=150)
ax = fig.add_subplot(111, projection='3d')
ax.set_facecolor('white')
fig.patch.set_facecolor('white')

n = len(intervals)
x_pos = np.arange(n)

width = 0.5
depth = 0.3

max_area = 50
max_pct = max(pct_before.max(), pct_after.max())

scale_factor = 0.85
pct_height_before = max_area * (pct_before / 100) * scale_factor
pct_height_after = max_area * (pct_after / 100) * scale_factor

# ===== 3. 柱状图 =====
bar_y_pos = 0.6

bars1 = ax.bar3d(x_pos - width/2, bar_y_pos, 0,
                 width, depth, area_before,
                 color='#B8A9C9', alpha=0.5, edgecolor='none')
bars2 = ax.bar3d(x_pos - width/2, bar_y_pos + depth, 0,
                 width, depth, area_after,
                 color='#00aaff', alpha=0.6, edgecolor='none')

# ===== 4. 占比折线 =====
line_y_pos = -0.3

ax.plot(x_pos, [line_y_pos]*n, pct_height_before,
        color='#B8A9C9', linewidth=2, marker='o', markersize=2,
        markerfacecolor='#B8A9C9', markeredgewidth=2, markeredgecolor='#B8A9C9',
        linestyle='--', label='作业前占比')

ax.plot(x_pos, [line_y_pos]*n, pct_height_after,
        color='#00aaff', linewidth=2, marker='o', markersize=2,
        markerfacecolor='white', markeredgewidth=2, markeredgecolor='#00aaff',
        linestyle='-.', label='作业后占比')

# ===== 5. 占比数值标签 =====
# step_label = max(1, n // 8)
# for i in range(0, n, step_label):
#     ax.text(x_pos[i], line_y_pos, pct_height_before[i] + max_area*0.015,
#             f'{pct_before[i]:.1f}%', color='#00aaff', fontsize=8, ha='center', va='bottom')
#     ax.text(x_pos[i], line_y_pos, pct_height_after[i] + max_area*0.015,
#             f'{pct_after[i]:.1f}%', color='#ff44aa', fontsize=8, ha='center', va='bottom')

# ===== 6. 从折线到地面的垂直虚线 =====
step_line = max(1, n // 15)
for i in range(0, n, step_line):
    ax.plot([x_pos[i], x_pos[i]], [line_y_pos, line_y_pos], [0, pct_height_before[i]],
            color='#00aaff', linewidth=0.5, alpha=0.2, linestyle=':')
    ax.plot([x_pos[i], x_pos[i]], [line_y_pos, line_y_pos], [0, pct_height_after[i]],
            color='#ff44aa', linewidth=0.5, alpha=0.2, linestyle=':')

# ===== 7. Y轴标签 =====
ax.text(-1.2, bar_y_pos + depth/2, max_area*0.05, '面积',
        color='black', fontsize=13, fontweight='bold', ha='right')
ax.text(-1.2, line_y_pos, max_area*0.05, '占比',
        color='black', fontsize=13, fontweight='bold', ha='right')

# ===== 8. Z轴刻度 =====
ax.set_zlim(0, max_area * 1.2)

# 面积刻度(黑色,步长5,从0到50)
z_ticks_area = np.arange(0, 51, 5)
ax.set_zticks(z_ticks_area)
ax.set_zticklabels([f'{int(v)}' for v in z_ticks_area], color='black', fontsize=9)

# 占比刻度(粉色):10%, 20%, 30%, 40%, 50%
pct_ticks = np.arange(10, 51, 10)  # 10, 20, 30, 40, 50
pct_z_positions = max_area * (pct_ticks / 100) * scale_factor

for pct_val, z_pos in zip(pct_ticks, pct_z_positions):
    ax.plot([-0.3, 0], [line_y_pos, line_y_pos], [z_pos, z_pos],
            color='black', linewidth=1.5, alpha=0.7)
    ax.text(-0.5, line_y_pos, z_pos, f'{pct_val:.0f}%',
            color='black', fontsize=8, ha='right', va='center')


# ===== 9. X轴美化 =====
tick_step = max(1, n // 20)
ax.set_xticks(x_pos[::tick_step])
ax.set_xticklabels([f'{intervals[i]:.1f}' for i in range(0, n, tick_step)],
                   rotation=45, color='black', fontsize=9)

ax.set_xlabel('NDVI', fontsize=14, color='black', fontweight='bold', labelpad=10)

# Y轴隐藏
ax.set_ylabel('')
ax.set_yticks([])
ax.spines['left'].set_visible(False)

# ===== 关键修改:Z轴标签字体大小为0 =====
ax.set_zlabel('', fontsize=0)  # 字体大小为0,彻底看不见

ax.tick_params(colors='black', labelsize=10)
ax.xaxis.label.set_color('black')

ax.spines['bottom'].set_color('#aaaaaa')
ax.spines['right'].set_color('#aaaaaa')

# ===== 关掉所有网格,然后手动加 X 和 Y 方向的网格线 =====
ax.grid(False)

# X方向网格线(竖直线,在底部z=0平面上)
for x in x_pos[::tick_step]:
    ax.plot([x, x], [-0.5, 1.0], [0, 0], color='#cccccc', linewidth=0.3, alpha=0.5, linestyle='--')

# Y方向网格线(水平线,在底部z=0平面上)
for y in [-0.3, 0.6]:
    ax.plot([0, n-1], [y, y], [0, 0], color='#cccccc', linewidth=0.3, alpha=0.5, linestyle='--')

# ===== 10. 视角 =====
ax.view_init(elev=30, azim=-55)

# # ===== 11. 标题和图例 =====
# ax.set_title('作业前后面积与占比 3D立体对比图谱', fontsize=22, fontweight='bold',
#              color='black', pad=30)

from matplotlib.patches import Patch
legend_elements = [
    Patch(facecolor='#B8A9C9', alpha=0.5, label='作业前面积'),
    Patch(facecolor='#00aaff', alpha=0.6, label='作业后面积'),
    Patch(facecolor='#B8A9C9', alpha=0.8, label='作业前占比'),
    Patch(facecolor='#00aaff', alpha=0.8, label='作业后占比')
]
ax.legend(handles=legend_elements, loc='upper left', fontsize=11,
          facecolor='white', edgecolor='black', labelcolor='black', framealpha=0.9)

plt.show()

图1

 

posted @ 2026-06-21 09:59  SuYue2990  Views(1)  Comments(0)    收藏  举报