【设计篇】35 # 如何让可视化设计更加清晰?

news2024/11/15 0:20:28

说明

【跟月影学可视化】学习笔记。

分清信息主次,建立视觉层次

用醒目的颜色突出显示数据,把被淡化的其他视觉元素当作背景。

比如:平均温度与露点的散点例子

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>平均温度与露点的散点</title>
    </head>
    <body>
        <div id="app" style="width: 1200px; height: 600px"></div>
        <script src="https://d3js.org/d3.v6.js"></script>
        <script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
        <script src="https://unpkg.com/@qcharts/core/dist/index.js"></script>
        <script>
            (async function () {
                // 通过 fetch 读取 csv 的数据
                const rawData = await (
                    await fetch("./data/beijing_2014.csv")
                ).text();
                console.log(rawData);
                // 使用 d3 的 csvParse 方法,将数据解析成 JSON 数组
                const data = d3.csvParse(rawData);
                
                const dataset = data.map(d => {
                    return {
                        temperature: Number(d['Temperature(Celsius)(avg)']),
                        tdp: Number(d['Dew Point(Celsius)(avg)']),
                        category: '平均气温与露点'
                    }
                });

                const { Chart, Scatter, Legend, Tooltip, Axis } = qcharts;
                // 创建图表(Chart)并传入数据
                const chart = new Chart({
                    container: "#app",
                });
                let clientRect = { bottom: 50 };
                chart.source(dataset, {
                    row: "category",
                    value: "temperature",
                    text: "tdp",
                });

                // 创建横、纵两个坐标轴(Axis)、提示(ToolTip)和一个图例(Legend)
                const scatter = new Scatter({
                    clientRect,
                    showGuideLine: true,
                });
                const toolTip = new Tooltip({
                    title: (data) => data.category,
                    formatter: (data) => {
                        return `温度:${data.value}C  露点:${data.tdp}% `
                    }
                });
                const legend = new Legend();
                const axisLeft = new Axis({ orient: "left", clientRect })
                    .style("axis", false)
                    .style("scale", false);
                const axisBottom = new Axis();
                // 将图形、坐标轴、提示和图例都添加到图表上
                chart.append([scatter, axisBottom, axisLeft, toolTip, legend]);
            })();
        </script>
    </body>
</html>

下面就是一个有鲜明视觉层次感的图表:

  • 使用比较鲜明的蓝色来突出图形
  • 用比较淡的灰黑色来显示左侧和下方的坐标轴
  • 用存在感最弱的辅助线背景来辅助用户更认真地阅读图表、理解数值

在这里插入图片描述

再此基础上可以添加曲线图来引导用户关注到平均气温与露点的正相关性特点

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>平均温度与露点的散点添加曲线</title>
    </head>
    <body>
        <div id="app" style="width: 1200px; height: 600px"></div>
        <script src="https://d3js.org/d3.v6.js"></script>
        <script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
        <script src="https://unpkg.com/@qcharts/core/dist/index.js"></script>
        <script>
            (async function () {
                // 通过 fetch 读取 csv 的数据
                const rawData = await (
                    await fetch("./data/beijing_2014.csv")
                ).text();
                console.log(rawData);
                // 使用 d3 的 csvParse 方法,将数据解析成 JSON 数组
                const data = d3.csvParse(rawData);

                const dataset = data.map((d) => {
                    return {
                        temperature: Number(d["Temperature(Celsius)(avg)"]),
                        tdp: Number(d["Dew Point(Celsius)(avg)"]),
                        category: "平均气温与露点",
                    };
                }).sort((a, b) => a.tdp - b.tdp);

                // 露点排序
                let dataset2 = [...dataset]

                // 对相同露点的温度进行分组
                dataset2 = dataset2.reduce((a, b) => {
                    let curr = a[a.length - 1];
                    if (curr && curr.tdp === b.tdp) {
                        curr.temperature.push(b.temperature);
                    } else {
                        a.push({
                            temperature: [b.temperature],
                            tdp: b.tdp,
                        });
                    }
                    return a;
                }, []);

                // 最后将露点平均温度计算出来
                dataset2 = dataset2.map((d) => {
                    d.category = "露点平均气温";
                    d.temperature = Math.round(
                        d.temperature.reduce((a, b) => a + b) /
                            d.temperature.length
                    );
                    return d;
                });

                console.log("最后将露点平均温度计算出来--->", dataset2)

                const { Chart, Scatter, Line, Legend, Tooltip, Axis } = qcharts;
                // 创建图表(Chart)并传入数据
                const chart = new Chart({
                    container: "#app",
                });
                let clientRect = { bottom: 50 };
                chart.source([...dataset, ...dataset2], {
                    row: "category",
                    value: "temperature",
                    text: "tdp",
                });

                const ds = chart.dataset;
                const d1 = ds.selectRows("平均气温与露点");
                const d2 = ds.selectRows("露点平均气温");

                // 散点图
                const scatter = new Scatter({
                    clientRect,
                    showGuideLine: true,
                }).source(d1);

                // 曲线图
                const line = new Line().source(d2);
                line.style("line", function (attrs, data, i) {
                    return { smooth: true, lineWidth: 3, strokeColor: "#0a0" };
                });
                line.style("point", function (attrs) {
                    return { display: "none" };
                });

                const toolTip = new Tooltip({
                    title: (data) => data.category,
                    formatter: (data) => {
                        return `温度:${data.value}C  露点:${data.tdp}% `;
                    },
                });
                const legend = new Legend();
                const axisLeft = new Axis({ orient: "left", clientRect })
                    .style("axis", false)
                    .style("scale", false);
                const axisBottom = new Axis();
                // 将图形、坐标轴、提示和图例都添加到图表上
                chart.append([scatter, line, axisBottom, axisLeft, toolTip, legend]);
            })();
        </script>
    </body>
</html>

效果如下图,可以清晰的看到曲线描绘气温与平均露点的关系,这样层次分明的图表,非常有助于理解图表上的信息

在这里插入图片描述

选择合适图表,直观表达信息

之前 32 节里的公园游客散点图,在表达某个单组变量的分布状况的时候,不是很直观,可以使用饼图进行处理

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>游客饼图</title>
        <style>
            html, body {
                width: 100%;
                height: 100%;
            }
            #container {
                width: 600px;
                height: 600px;
                display: flex;
                flex-wrap: wrap;
                justify-content: space-between;
            }

            #container > div {
                width: 300px;
                height: 300px;
                flex-shrink: 0;
            }
        </style>
    </head>
    <body>
        <div id="container">
            <div id="square"></div>
            <div id="sections"></div>
            <div id="garden"></div>
            <div id="playground"></div>
        </div>
        <script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
        <script src="https://unpkg.com/@qcharts/core@1.0.25/dist/index.js"></script>
        <script>
            const { Scene, Sprite, Polyline, SpriteSvg } = spritejs;
            (async function () {
                const data = await (await fetch("./data/park-people.json")).json();
                console.log(data);

                function count(d, dataset) {
                    let place;
                    if (d.x < 300 && d.y < 300) {
                        place = "square";
                    } else if (d.x >= 300 && d.y < 300) {
                        place = "sections";
                    } else if (d.x >= 300 && d.y >= 300) {
                        place = "garden";
                    } else {
                        place = "playground";
                    }
                    dataset[place] = dataset[place] || [
                        {
                            gender: "男游客",
                            people: 0,
                        },
                        {
                            gender: "女游客",
                            people: 0,
                        },
                    ];
                    if (d.gender === "f") {
                        dataset[place][0].people++;
                    } else {
                        dataset[place][1].people++;
                    }

                    return dataset;
                }

                function groupData(data) {
                    const dataset = {};

                    for (let i = 0; i < data.length; i++) {
                        const d = data[i];

                        if (d.time === 12) {
                            const p = count(d, dataset);
                        }
                    }

                    return dataset;
                }

                const dataset = groupData(data);
                console.log(dataset);

                const { Chart, Pie, Legend, Tooltip, theme } = qcharts;

                theme.set({
                    colors: ["#6a5acd", "#fa8072"],
                });

                Object.entries(dataset).forEach(([key, dataset]) => {
                    const chart = new Chart({
                        container: `#${key}`,
                    });

                    chart.source(dataset, {
                        row: "gender",
                        value: "people",
                        text: "gender",
                    });

                    const pie = new Pie({
                        radius: 0.7,
                        animation: {
                            duration: 700,
                            easing: "bounceOut",
                        },
                    });

                    const legend = new Legend({
                        orient: "vertical",
                        align: ["right", "center"],
                    });
                    const toolTip = new Tooltip();

                    chart.append([pie, legend, toolTip]);
                });
            })();
        </script>
    </body>
</html>

效果如下,饼图表示了公园内四个区域中男女游客的分布情况,可以看到饼图表示的结果非常简单和直观,但不足就是展示的信息很少,并且需要四个饼图。

在这里插入图片描述

如果要表示更多维度的信息,我们可以考虑使用嵌套饼图,将4张饼图合并起来

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>游客嵌套饼图</title>
        <style>
            html,
            body {
                width: 100%;
                height: 100%;
            }
            #container {
                width: 600px;
                height: 600px;
            }
        </style>
    </head>
    <body>
        <div id="container"></div>
        <script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
        <script src="https://unpkg.com/@qcharts/core@1.0.25/dist/index.js"></script>
        <script>
            const { Scene, Sprite, Polyline, SpriteSvg } = spritejs;
            (async function () {
                const data = await (
                    await fetch("./data/park-people.json")
                ).json();
                console.log(data);

                function count(d, dataset) {
                    let place;
                    if (d.x < 300 && d.y < 300) {
                        place = "square";
                    } else if (d.x >= 300 && d.y < 300) {
                        place = "sections";
                    } else if (d.x >= 300 && d.y >= 300) {
                        place = "garden";
                    } else {
                        place = "playground";
                    }
                    dataset[place] = dataset[place] || [
                        {
                            gender: "男游客",
                            people: 0,
                        },
                        {
                            gender: "女游客",
                            people: 0,
                        },
                    ];
                    if (d.gender === "f") {
                        dataset[place][0].people++;
                    } else {
                        dataset[place][1].people++;
                    }

                    return dataset;
                }

                function groupData(data) {
                    const dataset = {};

                    for (let i = 0; i < data.length; i++) {
                        const d = data[i];

                        if (d.time === 12) {
                            const p = count(d, dataset);
                        }
                    }

                    return dataset;
                }

                const dataset = [];
                Object.entries(groupData(data)).forEach(([place, d]) => {
                    d[0].place = `${place}: 男`;
                    d[1].place = `${place}: 女`;
                    dataset.push(...d);
                });
                console.log(dataset);

                const { Chart, Pie, Legend, Tooltip, theme } = qcharts;
                const chart = new Chart({
                    container: `#container`,
                });

                chart.source(dataset, {
                    row: "place",
                    value: "people",
                });
                const ds = chart.dataset;

                const pie = new Pie({
                    radius: 0.4,
                    pos: [0, 0],
                }).source(
                    ds.selectRows(
                        dataset
                            .filter((d) => d.gender === "女游客")
                            .map((d) => d.place)
                    )
                );

                const pie2 = new Pie({
                    innerRadius: 0.5,
                    radius: 0.7,
                }).source(
                    ds.selectRows(
                        dataset
                            .filter((d) => d.gender === "男游客")
                            .map((d) => d.place)
                    )
                );

                const legend = new Legend({
                    orient: "vertical",
                    align: ["right", "center"],
                });

                chart.append([pie2, pie, legend]);
            })();
        </script>
    </body>
</html>

可以看到嵌套饼图表达了男女游客分别在四个区域的分布情况,中间小的饼状图是女性在四个区域的分布情况,大的饼图是男性在四个区域的分布情况。

在这里插入图片描述

我们还可以使用南丁格尔玫瑰图(一种圆形的直方图)将游客性别和游客区域的分布情况融合在一起表现出来

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>游客南丁格尔玫瑰图</title>
        <style>
            html,
            body {
                width: 100%;
                height: 100%;
            }
            #container {
                width: 600px;
                height: 600px;
            }
        </style>
    </head>
    <body>
        <div id="container"></div>
        <script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
        <script src="https://unpkg.com/@qcharts/core@1.0.25/dist/index.js"></script>
        <script>
            const { Scene, Sprite, Polyline, SpriteSvg } = spritejs;
            (async function () {
                const data = await (
                    await fetch("./data/park-people.json")
                ).json();
                console.log(data);

                function count(d, dataset) {
                    let place;
                    if (d.x < 300 && d.y < 300) {
                        place = "square";
                    } else if (d.x >= 300 && d.y < 300) {
                        place = "sections";
                    } else if (d.x >= 300 && d.y >= 300) {
                        place = "garden";
                    } else {
                        place = "playground";
                    }
                    dataset[place] = dataset[place] || [
                        {
                            gender: "男游客",
                            people: 0,
                        },
                        {
                            gender: "女游客",
                            people: 0,
                        },
                    ];
                    if (d.gender === "f") {
                        dataset[place][0].people++;
                    } else {
                        dataset[place][1].people++;
                    }

                    return dataset;
                }

                function groupData(data) {
                    const dataset = {};

                    for (let i = 0; i < data.length; i++) {
                        const d = data[i];

                        if (d.time === 12) {
                            const p = count(d, dataset);
                        }
                    }

                    return dataset;
                }

                const dataset = [];
                Object.entries(groupData(data)).forEach(([place, d]) => {
                    d[0].place = place;
                    d[1].place = place;
                    dataset.push(...d);
                });
                console.log(dataset);

                const { Chart, PolarBar, Legend, Tooltip, theme } = qcharts;
                const chart = new Chart({
                    container: `#container`,
                });

                theme.set({
                    colors: ["#6a5acd", "#fa8072"],
                });

                chart.source(dataset, {
                    row: "gender",
                    value: "people",
                    text: "place",
                });
                const bar = new PolarBar({
                    stack: true,
                    radius: 0.8,
                    groupPadAngle: 15,
                }).style("pillar", {
                    strokeColor: "#FFF",
                    lineWidth: 1,
                });
                const tooltip = new Tooltip();
                const legend = new Legend({
                    orient: "vertical",
                    align: ["right", "center"],
                });

                chart.append([bar, tooltip, legend]);
            })();
        </script>
    </body>
</html>

我们可以看到南丁格尔玫瑰图能把人群在公园区域的分布和性别分布规律显示在一张图上,让更多的信息呈现在一张图表里,既节省空间又高效率地获取了更多信息,更加的直观。

在这里插入图片描述

改变图形属性,强化数据差异

比如股市中常用的蜡烛图,又叫做 K 线图。里面包含了许多其他有用的信息,能让用户从中分析出商品或者股票的价格走势,再做出相应的决策。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux上安装python3.7.4

1.wget下载python3安装包 wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz2.wget下载的压缩包默认在~目录下 3.进入~目录&#xff0c;直接解压压缩包 tar -xzvf Python-3.7.2.tgz4.进入python解压后的目录&#xff0c;为python配置环境&#xff0c;指定pytho…

CMMI-培训管理

培训管理&#xff08;Training Management, TM&#xff09;是指根据机构&#xff08;或项目&#xff09;的需求来制定培训计划&#xff0c;并监督该计划的实施&#xff0c;确保培训取得预期效果。培训管理过程域是SPP模型的重要组成部分。本规范阐述了培训管理过程域的两个主要…

头歌:UDP Ping程序实现 接收并转发消息

头歌平台&#xff1a;头歌实践教学平台 (educoder.net)任务描述本关任务&#xff1a;在 Ping 服务端程序框架中&#xff0c;完成接收数据&#xff0c;并回传给相应客户端。如何接收数据包套接字数据在 UDP 通信中使用sendto()函数发送 UDP 数据。将数据发送到套接字&#xff0c…

Parallel Processing in Python - Python 中的并行处理

并行处理可以增加程序完成的任务数量&#xff0c;从而减少整体处理时间。这些有助于处理大规模问题。 参考链接&#xff1a;Parallel Processing in Python - GeeksforGeeks 1 并行处理介绍 对于并行性&#xff0c;重要的是将问题划分为不依赖于其他子单元 (或较少依赖) 的子…

【Vue中使用Echarts】大屏可视化项目整体布局(pink老师vue 版)

文章目录一、效果展示二、基本的布局三、背景四、代码布局中遇到的一些问题一、效果展示 先看一下展示的效果&#xff0c;无论是尺寸多宽的屏幕&#xff0c;都会将内容显示完整&#xff0c;做到了正正的响应式。唯一不足的是图表中的样例&#xff0c;会随着图表的缩放而变换位…

35-剑指 Offer 37. 序列化二叉树

题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 …

第02讲:HTTP操作之ElasticSearch索引操作

3.1.1、索引操作 实验1&#xff1a;创建索引 对比关系型数据库&#xff0c;创建索引就等同于创建数据库 在 Postman 中&#xff0c;向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/shopping { "acknowledged"【响应结果】: true, # true操作成功 "shards…

npm与包

1、包 1.1、什么是包 Node.js 中的第三方模块又叫做包。就像电脑和计算机指的是相同的东西&#xff0c;第三方模块和包指的是同一个概念&#xff0c;只不过叫法不同。 1.2、包的来源 不同于 Node.js 中的内置模块与自定义模块&#xff0c;包是由第三方个人或团队开发出来的&a…

跟踪数据集汇总

文章目录DanceTrack 运动跟踪数据集简介转为Labelme标注的物体检测数据集格式WiderPerson行人检测数据集简介转为Labelme标注的物体检测数据集格式DanceTrack 运动跟踪数据集 简介 DanceTrack 是一个大规模的多对象跟踪数据集。用于在遮挡、频繁交叉、同样服装和多样化身体姿…

DVWA之SQL注入漏洞与防御

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 耳熟能详的SQ注入是什么&#xff1f; 关于SQL注入漏洞&#xff0c;维基百科是这样解释的 …

传统离散制造行业的9个维度,你知道吗?

制造业是国家的经济基础&#xff0c;是立国之本、兴国之器、强国之基&#xff1b;作为我国实体经济的主体&#xff0c;是国民经济体系的重要组成部分。按照产品制造工艺过程特点&#xff0c;制造业总体上可以分为离散型制造、流程型制造、混合型制造。离散制造包括家电、家居、…

2022亚太杯数学建模E题(1月补赛)

占个位置吧&#xff0c;更新E题的详细思路代码&#xff0c;文章末尾名片获取&#xff01;ABC题已更新 持续为更新参考思路 E题思路分析&#xff1a; 第一问都是一些基础的数据分析问题&#xff0c;使用题目给出的数据稍加整理归纳即可得出结论。 E题给了4张表格数据&#x…

MySQL调优-MySQL索引优化实战二

目录 MySQL调优-MySQL索引优化实战二 分页查询优化 >>常见的分页场景优化技巧&#xff1a; 1、根据自增且连续的主键排序的分页查询 2、根据非主键字段排序的分页查询 Join关联查询优化 MySQL的表关联常见有两种算法&#xff1a; 1.嵌套循环连接 Nested-Loop Join…

Python入门注释和变量

1.1计算机的基本原理 1.2 计算机的组成 2.1编程语言与python 2.2在 Welcome to Python.org 里面进入 无脑下一步下载 下载后打开cmd&#xff0c;输入名令Python显示下载的版本号 exit&#xff08;&#xff09;退出编辑 Pycharm无脑安装 社区版无需破解&#xff0c;专业版需破…

MATLAB 矩阵数据可视化imagesc 以及 如何多图共用一个 colorbar

当遇到需要查看一个二维矩阵数据中值的大小分布情况时可以使用 MATLAB 把矩阵以图像的形式展现出来&#xff0c;这样更直观。MATLAB 的可视化函数之一是 imagesc( )&#xff0c;还有其他的方法&#xff0c;这里只介绍下 imagesc。 目录 1、单一作图 2、同时绘制多图并共用 c…

谷粒学院——第十九章、数据同步_网关

Canal 数据同步&#xff08;了解&#xff09; 网关 API 网关介绍 API 网关出现的原因是微服务架构的出现&#xff0c;不同的微服务一般会有不同的网络地址&#xff0c;而外部客户端可能 需要调用多个服务的接口才能完成一个业务需求&#xff0c;如果让客户端直接与各个微服务…

vue+element对接第三方接口实现校园发帖网站“淘柳职”

一.前言 接上一篇博客《vueelementuijava 前后端分离实现学校帖子网站&#xff0c;仿照“淘柳职”学校大作业》 上一篇博客介绍的项目完全自带前、后端实现的&#xff0c;是一个完整的项目&#xff0c;现在作者在此基础上&#xff0c;利用已实现的前端&#xff0c;对接《淘柳职…

android12.0(S) DeviceOwner 应用默认授权(MDM 权限)

MDM(Mobile Device Manager) 通俗来讲就是管理设备使用 国内 MDM 服务商有 360 等 国外 MDM 服务商有 hexnode 等 当你在设备上配置了 DeviceOwner 后&#xff0c;状态栏下拉中会多出如下 关于 DeviceOwner 介绍可参考下面 Android DeviceOwner 应用的能力 Android Device…

品牌舆情监测系统简介,品牌舆情监测及应对方案?

品牌舆情监测是指通过观察和分析互联网和社交媒体上关于企业、产品或服务的信息&#xff0c;以了解消费者对企业、产品或服务的看法和感受。品牌舆情监测可以帮助企业了解消费者对企业、产品或服务的反馈&#xff0c;从而改进产品和服务&#xff0c;提高客户满意度。品牌舆情监…

【4.3】Ribbon饥饿加载

【4.3】Ribbon饥饿加载1 测试2 饥饿加载2.1 修改加载策略3 Ribbon负载均衡总结1 测试 重启Order服务&#xff0c;回到浏览器&#xff0c;打开浏览器的控制台&#xff0c;发起一次请求&#xff1a; 可以看到这次请求的耗时达到了390ms 再刷新一次&#xff1a; 可以看到这次时…