HsuHeinrich
用真心,分享最实用的技巧~
130篇原创内容
公众号
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
plt.rcParams.update({"font.family": "Roboto"})
以下数据如果有需要的同学可关注公众号HsuHeinrich,回复【数据可视化】自动获取~
data = pd.read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-01-26/plastics.csv")
data.head()
image-20240129172848518
公司塑料废物数据集: country:国家 year:年份 parent_company:公司名称 empty/hdpe/ldpe/o/pet/pp/ps/pvc:塑料类型,数值为改塑料类型的废物量
# 汇总计算各公司每年不同类型的塑料废物总量
types_of_plastic = ["empty", "hdpe", "ldpe", "o", "pet", "pp", "ps", "pvc"]
total_by_company = (
data
.groupby(["parent_company", "year"], dropna=True)[types_of_plastic]
.sum()
.reset_index()
)
total_by_company.head()
image-20240129172919149
def get_top_n_data(data, n):
'''
创建2020年塑料废物总量topN的公司数据
'''
# 计算2020年各公司的塑料废物总量(其实也可以按grand_total求和)
top_data = (
data
.query("year == 2020") # 筛选2020年数据
.melt( # 将长格式的df转为宽格式
id_vars=["parent_company", "year"], # 保留的列
value_vars=types_of_plastic, # 需要转化的列,即types_of_plastic列表中的列
var_name="type" # 转化后的列名,注意原始列对应的值默认存在value列
)
.groupby("parent_company")["value"] # 按公司汇总计算所有类型塑料废物
.sum() # 求和
.reset_index()
)
# 处理异常数据,将其公司名转为Unbranded_unknown
top_data["parent_company"] = np.where(
top_data["parent_company"].isin(["Unbranded", "null", "NULL", "#ERROR!"]),
"Unbranded_unknown",
top_data["parent_company"]
)
# 获取topN的公司名称
top_companies = list(top_data.sort_values("value", ascending=False)["parent_company"][:n])
# 将非topN的公司标记为Other
top_data["company_lumped"] = np.where(
top_data["parent_company"].isin(top_companies),
top_data["parent_company"],
"Other"
)
# 合并total_by_company与top_data
top_data = top_data.drop_duplicates(["parent_company", "company_lumped"]) # 删除重复数据(若重复保留第一条)
top_data = top_data.merge(total_by_company, on="parent_company") # inner join
# 计算total,不包含'empty'
top_data["total"] = top_data.loc[:, types_of_plastic[:]].sum(axis=)
return top_data
# 构造top30的公司数据
top_thirty = get_top_n_data(total_by_company, n=)
# 展示构造的某个公司数据
top_thirty.query('company_lumped == "Bakhresa Group"') # value为2020年塑料废物总量,total为每年除empty塑料废物总量
image-20240129172959277
# 计算每家公司各类型塑料废物总量
top_thirty = (
top_thirty
.groupby("company_lumped")[types_of_plastic]
.sum()
.reset_index()
)
# 每家公司各类型塑料废物横向求和
top_thirty["row_sum"] = top_thirty.loc[:, types_of_plastic].sum(axis=)
# 计算各类型塑料废物比例
top_thirty[types_of_plastic] = top_thirty[types_of_plastic].apply(lambda x: x / top_thirty["row_sum"])
# 将长数据转为宽数据
top_thirty = top_thirty.melt(
id_vars="company_lumped",
value_vars=types_of_plastic,
var_name="type",
value_name="proportion" # 将value列命名为proportion
)
top_thirty.head()
image-20240129173024645
# 对公司名称进行排序
# 公司名称列表
categories = list(top_thirty["company_lumped"].unique())
# 按名称小写排序
sorted_categories = sorted(categories, key=str.lower)
# 移除Other并追加到列表最后
sorted_categories.remove("Other")
sorted_categories.append("Other")
# 将company_lumped转为有序分类变量
top_thirty["company_lumped"] = pd.Categorical(
top_thirty["company_lumped"],
categories=sorted_categories,
ordered=True
)
# 将top_thirty排序
top_thirty = top_thirty.sort_values("company_lumped", ascending=False)
top_thirty.head()
image-20240129173041851
# 自定义调色板
COLORS = ["#0C2C84", "#225EA8", "#1D91C0", "#41B6C4", "#7FCDBB", "#C7E9B4", "#FFFFCC"]
# 利用COLORS列表创建自定义的颜色渐变
cmap = mcolors.LinearSegmentedColormap.from_list("colormap", COLORS, N=)
def plot_heatmap(ax):
# 迭代每个塑料类型
for i, plastic in enumerate(types_of_plastic):
# 获取当前塑料类型的数据
d = top_thirty[top_thirty["type"] == plastic]
# 制作散点图所需的x和y
y = d["company_lumped"]
x = [i] * len(y)
# proportion作为颜色(proportion作为颜色介于0和1之间,因此无需标准化)
color = cmap(d["proportion"])
# 绘制散点图
ax.scatter(x, y, color=color, s=)
# 美化轴
ax.set_frame_on(False) # 删除边框
ax.grid(alpha=0.4) # 设置网格线
ax.set_axisbelow(True) # 网格线位于底层
ax.set_xticks(np.arange(len(types_of_plastic))) # 设置x刻度位置
ax.set_xticklabels(types_of_plastic) # 设置x刻度标签
ax.tick_params(size=, colors="0.3") # 删除刻度线,并设置颜色为0.3来置灰
ax.set_xlabel("Type of plastic", loc="right") # 设置x轴标签
# 限制y轴范围
y_shrunk = 0.75
y_lower, y_upper = ax.get_ylim()
ax.set_ylim(y_lower + y_shrunk, y_upper - y_shrunk)
return ax
# 利用散点图绘制热图效果
fig, ax = plt.subplots(figsize=(, ))
plot_heatmap(ax);
output_15_0
# 自定义颜色,这里需要绘制top6公司的径向条形图
COMPANY_PALETTES = ["#81C4CA", "#468D96", "#103128", "#E83D5F", "#FA6E90", "#FCB16D"]
# 构造合适的数据
# 先获取top7的公司,因为存在Other
top_seven = get_top_n_data(total_by_company, n=)
# 长转宽
top_seven = top_seven.melt(
id_vars="company_lumped",
value_vars=types_of_plastic,
var_name="type",
value_name="amount"
)
# 删除Unbranded_unknown、Other
top_seven = top_seven[~top_seven["company_lumped"].isin(["Unbranded_unknown", "Other"])]
# 删除ps、pvc、empty等塑料类型
top_seven = top_seven[~top_seven["type"].isin(["ps", "pvc", "empty"])]
# 计算各公司各塑料类型的废物总量
top_seven = top_seven.groupby(["company_lumped", "type"]).sum().reset_index()
top_seven = top_seven.rename({"amount": "total"}, axis=) # 重命名字段amount
# 计算比例
top_seven["prop"] = top_seven["total"] / top_seven.groupby("company_lumped")["total"].transform("sum")
# 自定义函数
def style_polar_axis(ax):
'''
美化极坐标轴
'''
# 将0弧度的位置逆时针旋转90度,使图形的起点位于上方
ax.set_theta_offset(np.pi / )
# 绘制方向顺时针(默认逆时针)
ax.set_theta_direction(-1)
# 移除边框
ax.set_frame_on(False)
# 移除刻度标签
ax.set_xticklabels([])
# 设置范围
ax.set_ylim([, 4.5])
# 设置y的刻度位置
ax.set_yticks([, , , , , 4.5])
# 移除刻度标签
ax.set_yticklabels([])
# 设置网格线,并且让网格线半透明
ax.grid(alpha=0.4)
return ax
def add_labels_polar_axis(ax, color):
'''
添加坐标轴标签(塑料类型),并自定义颜色
'''
# 定义样式
bbox_dict = {
"facecolor": "w", "edgecolor": color, "linewidth": ,
"boxstyle": "round", "pad": 0.15
}
types_of_plastic = ["hdpe", "ldpe", "o", "pet", "pp"]
# 遍历塑料类型在轴上添加文本标签
for idx, plastic in enumerate(types_of_plastic):
ax.text(
, idx, plastic, color=color, ha="center", va="center",
fontsize=, bbox=bbox_dict
)
return ax
def plot_circular(axes):
'''
通过极坐标绘制多个公司的径向条形图。循环绘制在多个子图上
'''
axes_flattened = axes.ravel() # 将接收的子图对象数组进行降维,返回一个一维的子图对象数组
companies = top_seven["company_lumped"].unique() # 公司列表
# 迭代为每个公司绘制径向条形图
for i, company in enumerate(companies):
# 提取数据
d = top_seven[top_seven["company_lumped"] == company]
# 绘制在对应的子图位置上
ax = axes_flattened[i]
# 在第一个子图上,设置 y 轴的标签("Type of plastic")。
if i == :
ax.set_ylabel("Type of plastic", loc="top")
# 调用style_polar_axis函数美化轴
ax = style_polar_axis(ax)
# prop*2pi 极坐标系中的角度
proportions = d["prop"].values * ( * np.pi)
# y的位置
y_pos = np.arange(len(proportions))
# 计算所有子图的 x 和 y 值
x = np.linspace(, proportions, num=)
y = np.vstack([y_pos] * )
# 选择颜色
color = COMPANY_PALETTES[i]
# 在子图上绘制圆周
ax.plot(x, y, lw=, color=color, solid_capstyle="round")
# 添加标题
ax.set_title(company, pad=, color="0.3")
# 在每个公司的圆周上添加标签
ax = add_labels_polar_axis(ax, color)
return axes
# 初始化布局
fig, axes = plt.subplots(, , figsize=(, ), subplot_kw={"projection": "polar"})
axes = plot_circular(axes)
output_20_0
# 初始化布局
fig = plt.figure(figsize=(, ))
# 添加第一个网格布局:1*1
gs1 = fig.add_gridspec(nrows=, ncols=, left=0.25, right=0.5, top=0.85)
# 在上述网格图中绘制子图
ax = fig.add_subplot(gs1[])
# 绘制热图
plot_heatmap(ax)
# 创建一个空列表存放六个极坐标图的轴对象
axes = []
# 添加第一个网格布局:3*2
gs2 = fig.add_gridspec(nrows=, ncols=, left=0.55, right=0.95, hspace=0.25, top=0.85)
# 迭代每个单元格,以极坐标投影方式,将这些子图对象添加到 axes 列表中
for gs in gs2:
axes.append(fig.add_subplot(gs, projection="polar"))
# 将列表转换成 NumPy 数组,方便后续处理
axes = np.array(axes)
# 在对应的子图对象上绘制极坐标图
plot_circular(axes);
output_22_0
参考:Heatmap and Radial Barchart with Matplotlib[1]
共勉~
[1]
Heatmap and Radial Barchart with Matplotlib: https://python-graph-gallery.com/web-heatmap-and-radial-barchart-plastics/