第8章 高级可视化图表

探索更专业的数据可视化技术,包括等高线图、矢量场图、桑基图、树状图和华夫饼图等高级图表类型。

8.1 绘制等高线图

等高线图是一种二维图表,用于表示三维数据的表面。它通过连接相同数值的点形成等高线,常用于表示地形、气象数据、温度分布等连续变化的场数据。

等高线图在地理信息系统(GIS)、气象学、物理学等领域有广泛应用,可以直观地展示数据的分布和变化趋势。

加载中...
import numpy as np
import matplotlib.pyplot as plt

# 创建网格数据
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z = np.exp(-X**2 - Y**2)

# 绘制等高线图
fig, ax = plt.subplots()
CS = ax.contour(X, Y, Z)
ax.clabel(CS, inline=True, fontsize=10)
ax.set_title('等高线图示例')
plt.show()
// 创建等高线图数据
const size = 100;
const x = [];
const y = [];
const z = [];

for (let i = 0; i < size; i++) {
    x[i] = [];
    y[i] = [];
    z[i] = [];
    
    for (let j = 0; j < size; j++) {
        const xVal = -3 + j * 6 / size;
        const yVal = -2 + i * 4 / size;
        x[i][j] = xVal;
        y[i][j] = yVal;
        z[i][j] = Math.exp(-(xVal * xVal + yVal * yVal));
    }
}

// 使用Plotly绘制等高线图
const data = [{
    x: x[0],
    y: y.map(row => row[0]),
    z: z,
    type: 'contour',
    colorscale: 'Viridis'
}];

const layout = {
    title: '等高线图示例',
    autosize: true
};

Plotly.newPlot('contour-chart', data, layout);

8.2 绘制矢量场流线图

矢量场流线图用于显示向量场的方向和大小,常见于流体力学、电磁学、气象学等领域的可视化。流线表示向量场的流线,展示粒子在该场中的运动路径。

矢量场流线图可以帮助理解流体的流动模式、电磁场的分布情况,以及各种物理场的特性。

加载中...
import numpy as np
import matplotlib.pyplot as plt

# 创建网格
Y, X = np.mgrid[-3:3:100j, -3:3:100j]
# 计算向量场
U = -1 - X**2 + Y
V = 1 + X - Y**2

# 绘制流线图
fig, ax = plt.subplots()
ax.streamplot(X, Y, U, V, density=1.5, color='purple', linewidth=1)
ax.set_title('矢量场流线图')
plt.show()
// 创建流线图数据
const size = 30;
const x = [];
const y = [];
const u = [];
const v = [];

for (let i = 0; i < size; i++) {
    x[i] = [];
    y[i] = [];
    u[i] = [];
    v[i] = [];
    
    for (let j = 0; j < size; j++) {
        const xVal = -3 + j * 6 / size;
        const yVal = -3 + i * 6 / size;
        x[i][j] = xVal;
        y[i][j] = yVal;
        u[i][j] = -1 - xVal * xVal + yVal;
        v[i][j] = 1 + xVal - yVal * yVal;
    }
}

// 使用Plotly绘制流线图
const data = [{
    type: 'stream',
    x: x,
    y: y,
    u: u,
    v: v,
    colorscale: 'Viridis'
}];

const layout = {
    title: '矢量场流线图',
    autosize: true
};

Plotly.newPlot('streamplot-chart', data, layout);

8.3 绘制桑基图

桑基图是一种特定类型的流程图,用于展示能量、材料或成本在系统中的流动和分布。它使用箭头表示流量,箭头的宽度与流量大小成正比。

桑基图特别适合用于能源分析、成本分配、物流分析等场景,能够直观地展示资源在不同环节之间的流动和转化情况。

加载中...
import matplotlib.pyplot as plt
from matplotlib.sankey import Sankey

# 消费收入与支出数据
flows = [0.7, 0.3, -0.3, -0.1, -0.3, -0.1, -0.1, -0.1]
# 流的标签列表
labels = ["工资", "副业", "生活", "购物", "深造", "运动", "其他", "买书"]
# 流的方向
orientations = [1, 1, 0, -1, 1, -1, 1, 0]

# 创建桑基图
fig = plt.figure(figsize=(10, 6))
sankey = Sankey()
sankey.add(flows=flows, labels=labels, orientations=orientations)
sankey.finish()
plt.title('个人收支桑基图')
plt.show()
// 桑基图数据
const data = {
  type: "sankey",
  orientation: "h",
  node: {
    pad: 15,
    thickness: 15,
    line: {
      color: "black",
      width: 0.5
    },
    label: ["工资", "副业", "总收入", "生活", "购物", "深造", "运动", "其他", "买书", "总支出"]
  },
  link: {
    source: [0, 1, 2, 2, 2, 2, 2, 2],
    target: [2, 2, 3, 4, 5, 6, 7, 8],
    value: [0.7, 0.3, 0.3, 0.1, 0.3, 0.1, 0.1, 0.1]
  }
};

const layout = {
  title: "个人收支桑基图",
  font: {
    size: 10
  }
};

Plotly.newPlot('sankey-chart', [data], layout);

8.4 绘制树状图

树状图是一种分层数据可视化方法,通过嵌套矩形展示层次结构数据。矩形的大小表示数值大小,颜色可用于表示其他变量或类别。

树状图特别适合展示层次结构数据,如文件系统大小、组织结构、市场份额分布等,能够有效地利用空间展示大量数据。

加载中...
import plotly.graph_objects as go

# 创建树状图数据
fig = go.Figure(go.Treemap(
    labels=["公司", "研发部", "市场部", "销售部", "财务部", "人力部", 
           "前端组", "后端组", "测试组", "推广组", "品牌组",
           "国内销售", "海外销售", "会计组", "审计组"],
    parents=["", "公司", "公司", "公司", "公司", "公司",
            "研发部", "研发部", "研发部", "市场部", "市场部",
            "销售部", "销售部", "财务部", "财务部"],
    values=[100, 30, 25, 25, 10, 10,
           10, 12, 8, 12, 13,
           13, 12, 6, 4]
))

fig.update_layout(title_text="公司组织结构树状图")
fig.show()
// 创建树状图数据
const data = {
  type: "treemap",
  labels: ["公司", "研发部", "市场部", "销售部", "财务部", "人力部", 
           "前端组", "后端组", "测试组", "推广组", "品牌组",
           "国内销售", "海外销售", "会计组", "审计组"],
  parents: ["", "公司", "公司", "公司", "公司", "公司",
            "研发部", "研发部", "研发部", "市场部", "市场部",
            "销售部", "销售部", "财务部", "财务部"],
  values: [100, 30, 25, 25, 10, 10,
           10, 12, 8, 12, 13,
           13, 12, 6, 4],
  textinfo: "label+value+percent parent",
  branchvalues: "total"
};

const layout = {
  title: "公司组织结构树状图",
  margin: {l: 0, r: 0, b: 0, t: 30}
};

Plotly.newPlot('treemap-chart', [data], layout);

8.5 绘制华夫饼图

华夫饼图是一种使用网格矩阵来展示比例数据的可视化方法,通过不同颜色的方块表示不同的类别及其比例。它是一种直观展示百分比的替代方案。

华夫饼图特别适合展示有限的几个类别及其占比,如市场份额、投票结果、完成率等,比传统饼图更易于精确比较比例大小。

加载中...
import matplotlib.pyplot as plt
import numpy as np

# 数据
categories = ['产品A', '产品B', '产品C', '产品D', '其他']
values = [0.3, 0.25, 0.2, 0.15, 0.1]
colors = ['#FF5252', '#448AFF', '#69F0AE', '#FFD740', '#B388FF']

# 创建华夫饼图
fig, ax = plt.subplots(figsize=(8, 6))
waffle_size = 10
data = np.zeros(waffle_size * waffle_size)
color_map = []

# 填充数据
start_idx = 0
for i, (cat, val) in enumerate(zip(categories, values)):
    count = int(val * waffle_size * waffle_size)
    data[start_idx:start_idx + count] = i + 1
    color_map.extend([colors[i]] * count)
    start_idx += count

# 绘制热图
data = data.reshape(waffle_size, waffle_size)
cmap = plt.matplotlib.colors.ListedColormap(['white'] + colors)
im = ax.imshow(data, cmap=cmap)
ax.set_xticks(np.arange(-.5, waffle_size, 1), minor=True)
ax.set_yticks(np.arange(-.5, waffle_size, 1), minor=True)
ax.grid(which='minor', color='white', linestyle='-', linewidth=1)
ax.set_xticks([])
ax.set_yticks([])

# 添加图例
legend_elements = [plt.Rectangle((0,0),1,1, fc=color, edgecolor='none', label=f'{cat}: {val*100:.0f}%') 
                 for cat, val, color in zip(categories, values, colors)]
ax.legend(handles=legend_elements, bbox_to_anchor=(1.05, 1), loc='upper left')

plt.title('市场份额华夫饼图')
plt.tight_layout()
plt.show()
// 华夫饼图数据
const categories = ['产品A', '产品B', '产品C', '产品D', '其他'];
const values = [0.3, 0.25, 0.2, 0.15, 0.1];
const colors = ['#FF5252', '#448AFF', '#69F0AE', '#FFD740', '#B388FF'];
const size = 10;

// 创建热图数据
const data = [];
let startIdx = 0;

for (let i = 0; i < values.length; i++) {
    const count = Math.floor(values[i] * size * size);
    for (let j = 0; j < count; j++) {
        data.push(i + 1);
    }
    startIdx += count;
}

// 补充剩余数据为0
while (data.length < size * size) {
    data.push(0);
}

// 重塑为二维数组
const heatmapData = [];
for (let i = 0; i < size; i++) {
    heatmapData[i] = [];
    for (let j = 0; j < size; j++) {
        heatmapData[i][j] = data[i * size + j];
    }
}

// 使用Plotly绘制热图
const plotData = [{
    z: heatmapData,
    type: 'heatmap',
    colorscale: [
        [0, 'rgba(255,255,255,1)'],
        [1/(categories.length + 1), 'rgba(255,255,255,1)'],
        [2/(categories.length + 1), colors[0]],
        [3/(categories.length + 1), colors[1]],
        [4/(categories.length + 1), colors[2]],
        [5/(categories.length + 1), colors[3]],
        [1, colors[4]]
    ],
    showscale: false,
    xgap: 2,
    ygap: 2
}];

const layout = {
    title: '市场份额华夫饼图',
    xaxis: {showgrid: false, showticklabels: false},
    yaxis: {showgrid: false, showticklabels: false},
    margin: {l: 50, r: 150, b: 50, t: 50},
    height: 450,
    annotations: []
};

// 添加图例
for (let i = 0; i < categories.length; i++) {
    const percentage = (values[i] * 100).toFixed(0);
    layout.annotations.push({
        x: 1.05,
        y: 1 - (i * 0.1),
        xref: 'paper',
        yref: 'paper',
        text: ` ${categories[i]}: ${percentage}%`,
        showarrow: false,
        font: {size: 12}
    });
}

Plotly.newPlot('waffle-chart', plotData, layout);

8.6 绘制甘特图

甘特图是一种条形图,用于显示项目进度和任务安排。它以水平条形表示任务,条形的长度表示任务持续时间,位置表示开始和结束时间。

甘特图是项目管理中最常用的工具之一,能够清晰地展示项目的时间线、任务依赖关系和进度情况。

加载中...
import matplotlib.pyplot as plt
import numpy as np

# 任务数据
tasks = ['项目确定', '问卷设计', '试访', '问卷确定', '实地执行', 
         '数据录入', '数据分析', '报告提交']
start_days = [0, 1.5, 2.5, 3.5, 3, 5.5, 6, 7.5]
durations = [2, 1, 0.5, 0.5, 3, 1, 1.5, 0.5]
colors = ['#FF5252', '#448AFF', '#69F0AE', '#FFD740', '#B388FF', 
          '#FF4081', '#00BCD4', '#8BC34A']

# 创建甘特图
fig, ax = plt.subplots(figsize=(12, 6))

for i, (task, start, duration, color) in enumerate(zip(tasks, start_days, durations, colors)):
    ax.barh(i, duration, left=start, color=color, alpha=0.8)
    ax.text(start + duration/2, i, task, ha='center', va='center', color='white', fontweight='bold')

# 设置图表
ax.set_yticks(range(len(tasks)))
ax.set_yticklabels(tasks)
ax.set_xlabel('天数')
ax.set_title('项目甘特图')
ax.grid(axis='x', linestyle='--', alpha=0.6)

plt.tight_layout()
plt.show()
// 甘特图数据
const tasks = ['项目确定', '问卷设计', '试访', '问卷确定', '实地执行', 
               '数据录入', '数据分析', '报告提交'];
const startDays = [0, 1.5, 2.5, 3.5, 3, 5.5, 6, 7.5];
const durations = [2, 1, 0.5, 0.5, 3, 1, 1.5, 0.5];
const colors = ['#FF5252', '#448AFF', '#69F0AE', '#FFD740', '#B388FF', 
               '#FF4081', '#00BCD4', '#8BC34A'];

// 创建甘特图数据
const traces = [];
for (let i = 0; i < tasks.length; i++) {
    traces.push({
        x: [startDays[i], startDays[i] + durations[i]],
        y: [tasks[i], tasks[i]],
        mode: 'lines',
        line: {
            color: colors[i],
            width: 20
        },
        hoverinfo: 'text',
        text: `${tasks[i]}: 第${startDays[i]}天开始,持续${durations[i]}天`
    });
}

const layout = {
    title: '项目甘特图',
    xaxis: {
        title: '天数',
        range: [0, 10]
    },
    yaxis: {
        title: '任务',
        autorange: 'reversed'
    },
    margin: {l: 100, r: 50, b: 50, t: 50},
    height: 400
};

Plotly.newPlot('gantt-chart', traces, layout);

8.7 绘制漏斗图

漏斗图用于显示数据在一系列阶段的递减过程,常用于展示转化率、销售流程、筛选过程等。每个阶段的宽度表示数量或比例。

漏斗图特别适合分析销售转化率、用户流失率、招聘流程等场景,能够直观地展示每个阶段的转化情况。

加载中...
import matplotlib.pyplot as plt
import numpy as np

# 漏斗图数据
stages = ['访问商品', '加购物车', '生成订单', '支付订单', '完成交易']
values = [1000, 500, 300, 200, 150]
percentages = [100, 50, 30, 20, 15]

# 计算漏斗图的x坐标
x1 = np.array(values)
x2 = np.array((x1.max() - x1) / 2)
x3 = [i + j for i, j in zip(x1, x2)]
x3 = np.array(x3)
y = -np.sort(-np.arange(len(stages)))

# 创建漏斗图
fig, ax = plt.subplots(figsize=(10, 8))

# 绘制条形图
rects1 = ax.barh(y, x3, height=0.5, tick_label=stages, color='g', alpha=0.5)
# 绘制辅助条形图
rects2 = ax.barh(y, x2, height=0.5, color='w', alpha=1)

# 添加线条
ax.plot(x3, y, 'black', alpha=0.7)
ax.plot(x2, y, 'black', alpha=0.7)

# 添加百分比标签
for i in range(len(values)):
    text_x = x2[i] + (x3[i] - x2[i]) / 2 - 30
    text_y = y[i]
    ax.text(text_x, text_y, f'{percentages[i]:.2f}%', fontsize=12)

# 隐藏轴脊和刻度
ax.set_xticks([])
for direction in ['top', 'left', 'bottom', 'right']:
    ax.spines[direction].set_color('none')
ax.yaxis.set_ticks_position('none')

plt.title('购物流程漏斗图')
plt.tight_layout()
plt.show()
// 漏斗图数据
const stages = ['访问商品', '加购物车', '生成订单', '支付订单', '完成交易'];
const values = [1000, 500, 300, 200, 150];

// 计算百分比
const percentages = values.map((val, idx) => {
    return idx === 0 ? 100 : Math.round((val / values[0]) * 100);
});

// 创建漏斗图
const maxValue = Math.max(...values);
const x2 = values.map(val => (maxValue - val) / 2);
const x3 = values.map((val, idx) => val + x2[idx]);

// 使用Plotly绘制漏斗图
const data = [{
    type: 'funnel',
    y: stages,
    x: values,
    textinfo: 'value+percent initial',
    textposition: 'inside',
    marker: {
        color: ['#4E79A7', '#F28E2C', '#E15759', '#76B7B2', '#59A14F'],
        line: {color: 'white', width: 2}
    },
    connector: {line: {color: 'white', dash: 'dot', width: 3}}
}];

const layout = {
    title: '购物流程漏斗图',
    margin: {l: 100, r: 50, b: 50, t: 50},
    height: 400,
    font: {
        size: 12
    }
};

Plotly.newPlot('funnel-chart', data, layout);

本章总结

第8章介绍了多种高级可视化图表类型,包括等高线图、矢量场流线图、桑基图、树状图、华夫饼图、甘特图和漏斗图等。这些图表各有特点,适用于不同的数据类型和分析场景:

  • 等高线图:适用于展示三维表面数据,如地形、温度分布等
  • 矢量场流线图:适用于展示向量场,如流体流动、电磁场分布等
  • 桑基图:适用于展示流量和流向,如能源流向、资金流向等
  • 树状图:适用于展示层次结构数据,如文件系统、组织结构等
  • 华夫饼图:适用于展示比例数据,比传统饼图更易于精确比较
  • 甘特图:适用于项目管理,展示任务时间线和进度
  • 漏斗图:适用于展示转化率和流程,如销售漏斗、用户转化等

选择合适的图表类型是数据可视化的关键,需要根据数据特点和分析目标进行选择。通过本章的学习,读者应该能够根据不同的数据场景选择最适合的可视化方法。