1、柱状图(Bar Graph)
matlab代码
% 数据
group_values = [10, 15, 7, 25];
class_values = [20, 5, 30, 10];
% X轴标签
labels = {'1', '2', '3', '4'};
% 创建一个图形
figure;
% 创建第一个子图:组数据
subplot(1, 2, 1);
bar(group_values, 'FaceColor', 'orange');
title('组数据');
set(gca, 'XTickLabel', labels);
xlabel('组');
ylabel('值');
grid on;
% 创建第二个子图:类数据
subplot(1, 2, 2);
bar(class_values, 'FaceColor', 'yellow');
title('类数据');
set(gca, 'XTickLabel', labels);
xlabel('类');
ylabel('值');
grid on;
% 调整图形使其美观
suptitle('组和类的柱状图');
set(gcf, 'Position', [100, 100, 1200, 600]);
2、折线图(Line Graph)
matlab代码
% 生成细化的横轴数据
x = linspace(0, 10, 100); % 生成从0到10的100个均匀分布的数据点
% 计算纵轴数据
y1 = sin(x); % 正弦函数
y2 = cos(x); % 余弦函数
% 创建图形
figure;
% 绘制第一条折线(黄色加粗虚线)
plot(x, y1, 'y--', 'LineWidth', 2); % 'y--' 表示黄色虚线, 'LineWidth', 2 表示加粗
hold on; % 保持当前图形,以便绘制第二条折线
% 绘制第二条折线(绿色加粗虚线)
plot(x, y2, 'g--', 'LineWidth', 2); % 'g--' 表示绿色虚线, 'LineWidth', 2 表示加粗
% 设置轴范围
ylim([-1, 1]); % 纵轴范围从 -1 到 1
xlim([-1, 11]); % 横轴范围从 -1 到 11,为了使中心线更显著
% 添加横轴中心线
yline(0, 'k--', 'Center Line'); % 添加一条黑色虚线作为横轴中心线
% 添加标签和标题
xlabel('X轴');
ylabel('Y轴');
title('正弦函数与余弦函数的圆滑折线图');
% 添加图例
legend('正弦函数', '余弦函数');
% 显示网格
grid on;
% 保持图形显示
hold off;
3、饼图(Pie Chart)
matlab代码
% 生成随机数据
data = [10, 20, 30, 40];
% 生成饼图
figure;
pie(data);
% 添加标题
title('随机生成的饼图');
% 添加图例
legend('部分1', '部分2', '部分3', '部分4');
% 显示图形
grid off;
4、堆积柱状图(Stacked Bar Graph)
matlab代码
% 设置随机种子以保证结果可重复
rng(0);
% 生成随机数据
numCategories = 3; % 堆积类别的数量
numBars = 5; % 柱子的数量
data = rand( numBars, numCategories ) * 10; % 随机生成数据,数据范围为 0 到 10
% 绘制堆积柱状图
figure;
bar(data, 'stacked');
title('随机生成的堆积柱状图');
xlabel('柱子编号');
ylabel('值');
legend('类别 1', '类别 2', '类别 3');
grid on;
5、散点图(Scatter Plot)
matlab代码
% 设置随机种子以保证结果可重复
rng(0);
% 生成随机数据
numPoints = 100; % 散点的数量
x = rand(numPoints, 1) * 10; % 生成 x 坐标数据,范围为 0 到 10
y = rand(numPoints, 1) * 10; % 生成 y 坐标数据,范围为 0 到 10
sizes = rand(numPoints, 1) * 100; % 生成每个点的大小,范围为 0 到 100
colors = rand(numPoints, 1); % 生成每个点的颜色(灰度值)
% 绘制散点图
figure;
scatter(x, y, sizes, colors, 'filled');
title('随机生成的散点图');
xlabel('X 轴');
ylabel('Y 轴');
colorbar; % 显示颜色条以指示颜色的映射
grid on;
6、箱线图(Box Plot)
matlab代码
% 生成随机数据
numGroups = 5; % 组数
numObservations = 100; % 每组的观察数
data = randn(numObservations, numGroups);
% 绘制箱线图
figure;
boxplot(data, 'Labels', {'组1', '组2', '组3', '组4', '组5'});
title('随机生成的箱线图');
xlabel('组');
ylabel('值');
7、条形图(Histogram)
matlab代码
% 生成随机数据
numBars = 10; % 条形的数量
data = rand(numBars, 1); % 生成 [0, 1] 范围内的随机数
% 绘制条形图
figure;
bar(data);
title('随机生成的条形图');
xlabel('类别');
ylabel('值');
% 设置 x 轴刻度标签
xticks(1:numBars);
xticklabels(arrayfun(@(x) sprintf('条形%d', x), 1:numBars, 'UniformOutput', false));
8、生存分析曲线(Survival Curve)
matlab代码
% 生成随机生存数据
numSubjects = 100; % 受试者数量
time = sort(rand(numSubjects, 1) * 10); % 随机生存时间,范围 [0, 10]
status = randi([0, 1], numSubjects, 1); % 0 = censored, 1 = event occurred
% 使用 Kaplan-Meier 方法计算生存函数
[fx, tx] = ecdf(time, 'censoring', ~status);
% 绘制 Kaplan-Meier 曲线
figure;
stairs(tx, 1 - fx, 'LineWidth', 2);
title('随机生成的生存分析曲线');
xlabel('时间');
ylabel('生存概率');
grid on;
9、热图(Heatmap)
matlab代码
% 生成随机数据
rows = 10; % 热图的行数
cols = 15; % 热图的列数
data = rand(rows, cols); % 生成随机数据矩阵
% 绘制热图
figure;
imagesc(data); % 绘制数据矩阵的图像
colorbar; % 添加颜色条
title('随机生成的热图');
xlabel('列');
ylabel('行');
% 可选:设置坐标轴刻度
xticks(1:cols);
yticks(1:rows);
10、雷达图(Radar Chart / Spider Chart)
matlab代码
% 生成随机数据
numVariables = 6; % 雷达图的变量数量
data = rand(numVariables, 1); % 生成 [0, 1] 范围内的随机数据
% 计算角度
theta = linspace(0, 2*pi, numVariables+1); % 从0到2π的角度
data = [data; data(1)]; % 使数据环绕一圈
% 绘制雷达图
figure;
polarplot(theta, data, '-o', 'LineWidth', 2);
title('随机生成的雷达图');
% 设置极坐标轴的范围
ax = gca;
ax.ThetaTick = 0:360/numVariables:360; % 设置角度刻度
ax.ThetaTickLabel = arrayfun(@(x) sprintf('变量%d', x), 1:numVariables, 'UniformOutput', false);
ax.RLim = [0 1]; % 设置半径范围
ax.RTick = [0.2 0.4 0.6 0.8 1]; % 设置半径刻度
% 可选:显示数据值
for i = 1:numVariables
text(theta(i), data(i) + 0.05, sprintf('%.2f', data(i)), 'HorizontalAlignment', 'center');
end
11、面积图(Area Chart)
matlab代码
% 生成随机数据
numCategories = 5; % 数据系列的数量
numPoints = 10; % 数据点的数量
x = 1:numPoints; % x 轴数据点
data = rand(numPoints, numCategories); % 生成随机数据矩阵
% 绘制面积图
figure;
area(x, data, 'LineWidth', 1.5);
% 添加图形标题和轴标签
title('随机生成的面积图');
xlabel('类别');
ylabel('值');
% 添加图例
legend(arrayfun(@(i) sprintf('系列%d', i), 1:numCategories, 'UniformOutput', false));
% 格式化图形
grid on;
12、气泡图(Bubble Chart)
matlab代码
% 生成随机数据
n = 100; % 点的数量
x = rand(n, 1) * 100; % x 坐标
y = rand(n, 1) * 100; % y 坐标
sizes = rand(n, 1) * 1000; % 气泡的大小
% 创建气泡图
figure;
scatter(x, y, sizes, 'filled', 'MarkerEdgeColor', 'k', 'MarkerFaceColor', 'b');
% 设置图表属性
xlabel('X 轴');
ylabel('Y 轴');
title('随机生成的气泡图');
grid on;
% 显示图形
axis([0 100 0 100]);
13、帕累托图(Pareto Chart)
matlab代码
% 随机生成示例数据
numCategories = 10; % 类别数量
categories = arrayfun(@(x) sprintf('Cat %d', x), 1:numCategories, 'UniformOutput', false);
values = randi([1, 100], numCategories, 1); % 随机生成整数值
% 对数据进行排序
[sortedValues, sortIdx] = sort(values, 'descend');
sortedCategories = categories(sortIdx);
% 计算累积百分比
cumulativeValues = cumsum(sortedValues);
totalValue = sum(sortedValues);
cumulativePercent = (cumulativeValues / totalValue) * 100;
% 创建帕累托图
figure;
yyaxis left;
bar(sortedCategories, sortedValues, 'FaceColor', 'b');
ylabel('频率');
title('随机生成的帕累托图');
yyaxis right;
plot(sortedCategories, cumulativePercent, '-o', 'LineWidth', 2, 'Color', 'r');
ylabel('累积百分比');
% 设置图表属性
xlabel('类别');
legend('频率', '累积百分比', 'Location', 'northwest');
grid on;
xtickangle(45); % 使 x 轴标签倾斜,以避免重叠
14、甘特图(Gantt Chart)
matlab代码
% 随机生成示例数据
numTasks = 8; % 任务数量
taskNames = arrayfun(@(x) sprintf('Task %d', x), 1:numTasks, 'UniformOutput', false);
% 随机生成任务开始时间和持续时间
startTimes = randi([1, 30], numTasks, 1); % 随机开始时间
durations = randi([1, 10], numTasks, 1); % 随机持续时间
% 计算任务结束时间
endTimes = startTimes + durations;
% 创建甘特图
figure;
hold on;
for i = 1:numTasks
barh(i, durations(i), 'BaseValue', startTimes(i), 'FaceColor', 'b');
end
% 设置图表属性
set(gca, 'YTick', 1:numTasks, 'YTickLabel', taskNames);
xlabel('时间');
ylabel('任务');
title('随机生成的甘特图');
grid on;
15、瀑布图(Waterfall Chart)
matlab代码
% 生成随机数据
numCategories = 10; % 数据类别数量
categories = arrayfun(@(x) sprintf('Category %d', x), 1:numCategories, 'UniformOutput', false);
values = randi([-10, 10], numCategories, 1); % 随机生成增减数据
% 计算每一步的累计值
cumulativeValues = cumsum(values);
% 创建瀑布图
figure;
hold on;
% 绘制瀑布图的条形
for i = 1:numCategories
if i == 1
% 第一个条形图
bar(i, values(i), 'FaceColor', 'b', 'EdgeColor', 'k');
line([i, i], [0, values(i)], 'Color', 'k', 'LineStyle', '--');
else
% 其他条形图
bar(i, values(i), 'FaceColor', 'b', 'EdgeColor', 'k');
line([i, i], [cumulativeValues(i-1), cumulativeValues(i-1) + values(i)], 'Color', 'k', 'LineStyle', '--');
end
end
% 添加累计线
plot(1:numCategories, cumulativeValues, '-o', 'LineWidth', 2, 'Color', 'r');
% 设置图表属性
set(gca, 'XTick', 1:numCategories, 'XTickLabel', categories);
xlabel('类别');
ylabel('值');
title('随机生成的瀑布图');
grid on;
16、森林图(Forest Plot)
matlab代码
% 生成随机数据
numStudies = 10; % 研究数量
studyNames = arrayfun(@(x) sprintf('Study %d', x), 1:numStudies, 'UniformOutput', false);
% 随机生成效应大小和置信区间
effectSizes = randn(numStudies, 1); % 随机生成效应大小
lowerCI = effectSizes - randn(numStudies, 1) * 0.5; % 随机生成下限置信区间
upperCI = effectSizes + randn(numStudies, 1) * 0.5; % 随机生成上限置信区间
% 创建森林图
figure;
hold on;
% 绘制效应大小和置信区间
for i = 1:numStudies
plot([lowerCI(i), upperCI(i)], [i, i], 'k-', 'LineWidth', 2); % 置信区间
plot(effectSizes(i), i, 'ko', 'MarkerFaceColor', 'b'); % 效应大小
end
% 绘制参考线
plot(xlim, [0, numStudies+1], 'k--'); % 参考线
% 设置图表属性
set(gca, 'YTick', 1:numStudies, 'YTickLabel', studyNames, 'YDir', 'reverse');
xlabel('效应大小');
ylabel('研究');
title('随机生成的森林图');
grid on;
17、桑基图(Sankey Diagram)
matlab代码
% 准备桑基图数据
sankey_data = struct;
sankey_data.nodes = {'A', 'B', 'C', 'D'};
sankey_data.links = [1, 2, 10; % A -> B, 流量 10
1, 3, 5; % A -> C, 流量 5
2, 3, 15; % B -> C, 流量 15
3, 4, 20];% C -> D, 流量 20
% 创建桑基图
figure;
hold on;
% 绘制节点
nNodes = length(sankey_data.nodes);
angles = linspace(0, 2*pi, nNodes+1);
xNodes = cos(angles(1:end-1));
yNodes = sin(angles(1:end-1));
scatter(xNodes, yNodes, 100, 'filled', 'k');
% 绘制节点标签
for i = 1:nNodes
text(xNodes(i), yNodes(i), sankey_data.nodes{i}, 'HorizontalAlignment', 'center');
end
% 绘制流量
for i = 1:size(sankey_data.links, 1)
src = sankey_data.links(i, 1);
tgt = sankey_data.links(i, 2);
flow = sankey_data.links(i, 3);
% 计算流向箭头的起点和终点
x1 = xNodes(src);
y1 = yNodes(src);
x2 = xNodes(tgt);
y2 = yNodes(tgt);
% 绘制箭头
quiver(x1, y1, x2-x1, y2-y1, flow, 'LineWidth', 2, 'MaxHeadSize', 2);
end
% 设置图形属性
axis equal;
xlabel('X轴');
ylabel('Y轴');
title('桑基图示例');
hold off;
18、树状图(Tree Diagram / Dendrogram)
matlab代码
% 随机生成树状图的数据
% 节点数量
numNodes = 10;
% 随机生成一个树的边列表
edges = [];
for i = 2:numNodes
% 随机选择一个父节点
parentNode = randi([1, i-1]);
edges = [edges; parentNode, i];
end
% 创建图形对象
G = graph(edges(:,1), edges(:,2));
% 绘制树状图
figure;
h = plot(G, 'Layout', 'layered');
title('随机生成的树状图');
% 使图形更具层次感
h.NodeLabel = {}; % 去掉节点标签
h.EdgeColor = [0.5 0.5 0.5]; % 边的颜色
h.NodeColor = [0.1 0.2 0.5]; % 节点的颜色
h.NodeCData = 1:numNodes; % 节点的颜色数据
% 调整节点的位置,使其呈现树状结构
h.XData = h.XData * 0.5; % 缩放X轴位置
h.YData = h.YData * 1.5; % 扩展Y轴位置
% 添加标签
labelnodes = num2cell(1:numNodes);
h.NodeLabel = labelnodes;
% 设置轴属性
axis off;
19、曼哈顿图(Manhattan Plot)
matlab代码
% 随机生成曼哈顿图的数据
numPoints = 1000; % 数据点数量
numChromosomes = 22; % 染色体数量
% 随机生成数据
chromosomes = randi(numChromosomes, numPoints, 1); % 随机分配染色体
positions = randi(1e6, numPoints, 1); % 随机生成位置
pValues = rand(numPoints, 1); % 随机生成p值
% 计算-log10(p值)
negLogPValues = -log10(pValues);
% 绘制曼哈顿图
figure;
hold on;
% 绘制每个染色体的数据
colors = lines(numChromosomes); % 为每个染色体分配不同的颜色
for i = 1:numChromosomes
% 选择当前染色体的数据
idx = chromosomes == i;
scatter(positions(idx), negLogPValues(idx), 10, colors(i, :), 'filled');
end
% 添加图形标题和标签
xlabel('位置');
ylabel('-log_{10}(p值)');
title('随机生成的曼哈顿图');
% 设置X轴的刻度
xlim([0 1e6]);
xticks([0 2.5e5 5e5 7.5e5 1e6]);
xticklabels({'0', '2.5e5', '5e5', '7.5e5', '1e6'});
% 设置Y轴的刻度
ylim([0 max(negLogPValues) + 1]);
yticks(0:5:max(negLogPValues) + 1);
% 添加图例
legend(arrayfun(@(x) sprintf('染色体 %d', x), 1:numChromosomes, 'UniformOutput', false), 'Location', 'best');
hold off;
20、小提琴图(Violin Plot)
matlab代码
% 随机生成数据
numGroups = 4; % 分组数量
numPointsPerGroup = 100; % 每组的数据点数量
% 随机生成数据
data = cell(numGroups, 1);
for i = 1:numGroups
data{i} = randn(numPointsPerGroup, 1) + i; % 每组数据随机生成
end
% 创建小提琴图
figure;
hold on;
% 绘制每组数据的小提琴图
for i = 1:numGroups
% 计算核密度估计
[f, xi] = ksdensity(data{i});
% 绘制小提琴图
fill([-f; flipud(f)] + i, [xi; flipud(xi)], 'k', 'FaceAlpha', 0.3, 'EdgeColor', 'none');
end
% 绘制中位线
for i = 1:numGroups
% 计算中位数
medianValue = median(data{i});
% 绘制中位线
plot([i, i], [min(data{i}), max(data{i})], 'k--', 'LineWidth', 1.5);
end
% 添加图形标题和标签
xlabel('组别');
ylabel('值');
title('随机生成的小提琴图');
% 设置x轴的刻度
set(gca, 'XTick', 1:numGroups);
set(gca, 'XTickLabel', arrayfun(@num2str, 1:numGroups, 'UniformOutput', false));
% 设置y轴的刻度
ylim([min(cellfun(@min, data)) - 1, max(cellfun(@max, data)) + 1]);
hold off;
21、关联图(Correlation Matrix)
matlab代码
% 随机生成数据
numVars = 5; % 变量数量
numSamples = 100; % 样本数量
% 生成随机数据
data = randn(numSamples, numVars);
% 计算相关矩阵
correlationMatrix = corr(data);
% 创建图形窗口
figure;
% 绘制散点图矩阵
subplot(1, 2, 1);
gplotmatrix(data, [], [], 'k', '.', 1, 'on', [], [], {'Var 1', 'Var 2', 'Var 3', 'Var 4', 'Var 5'});
title('散点图矩阵');
% 绘制相关矩阵热图
subplot(1, 2, 2);
heatmap(correlationMatrix, 'Title', '相关矩阵', 'XDisplayLabels', arrayfun(@(x) sprintf('Var %d', x), 1:numVars, 'UniformOutput', false), 'YDisplayLabels', arrayfun(@(x) sprintf('Var %d', x), 1:numVars, 'UniformOutput', false));
% 调整图形布局
sgtitle('关联图');
22、时间序列图(Time Series Plot)
matlab代码
% 随机生成时间序列数据
numSeries = 3; % 时间序列的数量
numPoints = 100; % 每个时间序列的数据点数量
% 生成时间轴
time = (1:numPoints)';
% 生成随机时间序列数据
data = zeros(numPoints, numSeries);
for i = 1:numSeries
% 随机生成时间序列数据
data(:, i) = cumsum(randn(numPoints, 1)); % 累加和生成随机时间序列
end
% 创建图形窗口
figure;
% 绘制每个时间序列
hold on;
colors = lines(numSeries); % 为每个时间序列分配不同的颜色
for i = 1:numSeries
plot(time, data(:, i), 'Color', colors(i, :), 'LineWidth', 1.5);
end
% 添加图形标题和标签
xlabel('时间');
ylabel('值');
title('随机生成的时间序列图');
% 添加图例
legend(arrayfun(@(x) sprintf('序列 %d', x), 1:numSeries, 'UniformOutput', false), 'Location', 'best');
% 设置网格
grid on;
hold off;
23、相干图(Cohort Chart)
matlab代码
% 设置随机种子以获得可重复的结果
rng(0);
% 生成随机信号
numPoints = 1024; % 数据点数量
Fs = 1000; % 采样频率 (Hz)
t = (0:numPoints-1) / Fs; % 时间向量
% 生成两个随机信号
signal1 = randn(1, numPoints);
signal2 = randn(1, numPoints);
% 计算信号的相干性
window = hamming(256); % 窗口函数
noverlap = 128; % 窗口重叠
nfft = 512; % FFT点数
[coherence, freq] = mscohere(signal1, signal2, window, noverlap, nfft, Fs);
% 创建图形窗口
figure;
% 绘制相干图
plot(freq, coherence, 'LineWidth', 1.5);
xlabel('频率 (Hz)');
ylabel('相干性');
title('随机信号的相干图');
grid on;
24、直方图
matlab代码
% 设定随机数的数量
numDataPoints = 1000;
% 生成随机数据(例如,均匀分布的随机数)
data = rand(numDataPoints, 1);
% 绘制直方图
figure;
histogram(data, 30); % 30 是直方图的箱数(bins),可以根据需要调整
title('随机数据的直方图');
xlabel('值');
ylabel('频数');
25、组合箱线图和散点图
matlab代码
load carsmall;
boxplot(MPG, Origin, 'PlotStyle', 'compact');
hold on;
scatter(1 + 0.1 * randn(size(MPG)), MPG, 'k', 'filled');
title('组合箱线图和散点图');
xlabel('产地');
ylabel('每加仑英里数');
grid on;
saveas(gcf, '组合箱线图和散点图.jpg');
26、平行坐标图
matlab代码
% 设定随机数的数量和维度
numDataPoints = 100;
numDimensions = 5;
% 生成随机数据
data = rand(numDataPoints, numDimensions);
% 生成随机数据的分类标签(可选)
% 对于不同类别的数据,可以给每个数据点指定一个标签
labels = randi([1, 3], numDataPoints, 1); % 生成 1 到 3 的随机整数
% 绘制平行坐标图
figure;
parallelcoords(data, 'Group', labels);
% 设置图形属性
title('随机数据的平行坐标图');
xlabel('维度');
ylabel('值');
% 添加图例
legend('类别 1', '类别 2', '类别 3');
27、交互式散点图
matlab代码
load carsmall;
g = gscatter(Weight, MPG, Model_Year);
xlabel('重量');
ylabel('每加仑英里数');
title('交互式散点图');
legend('Location', 'best');
grid on;
% 添加数据提示
datacursormode on;
saveas(gcf, '交互式散点图.jpg');
28、直方图矩阵
matlab代码
% 设置随机数种子以确保结果可重复
rng(0);
% 参数设置
numRows = 2; % 直方图矩阵的行数
numCols = 3; % 直方图矩阵的列数
numBins = 10; % 每个直方图的箱数
% 生成随机数据
data = cell(numRows, numCols);
for i = 1:numRows
for j = 1:numCols
% 生成随机数据
data{i, j} = randn(100, 1) * (i + j); % 数据根据行列位置调整标准差
end
end
% 创建直方图矩阵
figure;
for i = 1:numRows
for j = 1:numCols
% 计算每个直方图的位置
subplot(numRows, numCols, (i-1)*numCols + j);
histogram(data{i, j}, numBins);
% 设置图表属性
title(sprintf('Histogram %d,%d', i, j));
xlabel('值');
ylabel('频率');
grid on;
end
end
% 设置整体标题
sgtitle('直方图矩阵');
29、多变量高斯分布图
matlab代码
mu = [1 2];
sigma = [2 0.5; 0.5 1];
x1 = linspace(-3, 5, 100);
x2 = linspace(-1, 5, 100);
[X1, X2] = meshgrid(x1, x2);
F = mvnpdf([X1(:) X2(:)], mu, sigma);
F = reshape(F, length(x2), length(x1));
figure;
surf(x1, x2, F);
shading interp;
colormap jet;
colorbar;
title('多变量高斯分布图');
xlabel('X轴');
ylabel('Y轴');
zlabel('概率密度');
grid on;
saveas(gcf, '多变量高斯分布图.jpg');
30、带误差条的柱状图
matlab代码
% 随机生成示例数据
numCategories = 10; % 类别数量
categories = arrayfun(@(x) sprintf('Category %d', x), 1:numCategories, 'UniformOutput', false);
% 随机生成柱状图数据和误差
values = randn(numCategories, 1) * 10 + 50; % 随机生成柱状图数据
errors = rand(numCategories, 1) * 5; % 随机生成误差值
% 创建柱状图
figure;
barValues = bar(values, 'FaceColor', 'b', 'EdgeColor', 'k'); % 绘制柱状图
hold on;
% 添加误差条
errorbar(barValues.XData, values, errors, 'k', 'LineStyle', 'none', 'LineWidth', 1.5);
% 设置图表属性
set(gca, 'XTick', 1:numCategories, 'XTickLabel', categories);
xlabel('类别');
ylabel('值');
title('随机生成的带误差条的柱状图');
grid on;
标签:10,SCI,title,30,生成,matlab,data,随机
From: https://blog.csdn.net/m0_57407372/article/details/140609037