40、数据可视化:美国财富分配的直观呈现

数据可视化:美国财富分配的直观呈现

1. 图像文件输出

当我们对直方图满意后,往往希望输出高质量的版本。在Quil中,可通过在 setup 函数里添加 q/save 调用并传入文件名,这样Quil会同时将内容输出到文件和屏幕。生成图像的格式取决于文件名后缀,具体如下:
| 文件名后缀 | 图像格式 |
| — | — |
| .tif | TIFF文件 |
| .jpg | JPEG文件 |
| .png | PNG文件 |
| .tga | TARGA文件 |

以下是一个示例代码:

(defn draw-filled-grid [{:keys [n-bins size fill-fn]}]
  (let [[width height] size
        x-scale (/ width n-bins)
        y-scale (/ height n-bins)
        setup (fn []
                (doseq [x (range n-bins)
                        y (range n-bins)
                        :let [x-pos (* x x-scale)
                              y-pos (- height
                                       (* (inc y) y-scale))]]
                  (q/fill (fill-fn x y))
                  (q/rect x-pos y-pos x-scale y-scale))
                  (q/save "heatmap.png"))]
    (q/sketch :setup setup :size size)))

我们还能输出为PDF格式,后续可视化会展示相关内容。

2. 用于沟通的可视化

作为数据科学家,工作中常需与不同人群交流。亲密的同事和经理或许能读懂并解读Incanter图表,但这些图表可能难以打动CEO。有时我们还需与公众沟通。

无论何种情况,我们应专注于制作简单而强大的可视化图表,同时不牺牲数据的完整性。缺乏统计训练并不妨碍人们理解微妙细致的观点,我们应尊重受众的智慧。数据科学家面临的挑战是找到能有效传达信息的表达方式。

3. 财富分配可视化

我们要创建的可视化图表源自在线视频《美国财富不平等》(https://www.youtube.com/watch?v=QPKKQnijnsM )中的一个图表。该视频由匿名电影制作人Politizane制作,在YouTube上获得了超过1600万的点击量。

我们使用的第一组数据集来自加州大学圣克鲁兹分校心理学和社会学研究教授G. William Domhoff的文章《财富、收入和权力》(http://www2.ucsc.edu/whorulesamerica/power/wealth.html )。文章中的一张饼图展示了2010年美国人们的金融净资产细分情况:
| 百分位 | 2010年总金融财富 |
| — | — |
| 0 - 79 | 5% |
| 80 - 89 | 11% |
| 90 - 95 | 13% |
| 96 - 99 | 30% |
| 100 | 42% |

这张饼图引人关注有几个原因。首先,超过40%的金融财富被一小部分人拥有,这一概念难以理解。其次,饼图的每一块不仅代表着财富数量的巨大差异,也代表着人口数量的巨大差异,从1%的人口到80%的人口。而且,饼图通常很难解读,这张图更是如此。

一般来说,饼图不是呈现数据的好方式,即使总量在概念上代表整体的一部分。作者兼程序员Steve Fenton在https://www.stevefenton.co.uk/2009/04/pie-charts-are-bad/ 记录了很多原因并提供了合适的替代方案。

我们可以将这些数据重新解读为柱状图,以使其更易理解。以下是创建柱状图的代码:

(defn ex-10-10 []
  (let [categories ["0-79" "80-89" "90-95" "96-99" "100"]
        percentage [5      11      13      30      42   ]]
    (-> (c/bar-chart categories percentage
                     :x-label "Category"
                     :y-label "% Financial Wealth")
        (i/view))))

这个柱状图比饼图有所改进,更易于比较不同类别的相对大小。但仍存在一个显著问题:每个类别代表的人口数量差异巨大。左边的柱子代表80%的人口,而右边的柱子仅代表1%的人口。

为了让数据更易理解,我们可以将总数分成100个相等的单位,每个单位代表人口的一个百分位。每个柱子的宽度可以根据其所代表的百分位数进行调整,同时保持其面积不变。以下是实现代码:

(def wealth-distribution
  (concat (repeat 80 (/ 5  80))
          (repeat 10 (/ 11 10))
          (repeat 5  (/ 13 5))
          (repeat 4  (/ 30 4))
          (repeat 1  (/ 42 1)))))
(defn ex-10-11 []
  (let [categories (range (count wealth-distribution))]
    (-> (c/bar-chart categories wealth-distribution
                     :x-label "Percentile"
                     :y-label "% Financial Wealth")
        (i/view))))

通过这个简单的转换,我们能更好地理解真实的财富分配情况。每个柱子现在代表相等比例的人口,柱子的面积代表该百分位所拥有的财富比例。

4. 用Quil让数据生动起来

上一节的转换得到的图表过于鲜明地显示了财富分配的极端差异,除了最大的柱子外,其他柱子很难解读。一种解决方案是使用对数刻度或双对数刻度显示数据,但假设我们的目标受众是普通公众,这种方法可能不太合适。

之前的图表问题在于最右边的柱子太大,掩盖了其他所有柱子,80%的面积仅由几个像素表示。接下来,我们将使用Quil制作一个更合理利用空间、同时保持图表完整性的可视化图表。

5. 绘制不同宽度的柱子

我们将分阶段构建可视化图表。首先定义一些常量,以便根据草图的尺寸生成绘图指令:

(def plot-x 56)
(def plot-y 60)
(def plot-width 757)
(def plot-height 400)
(def bar-width 7)

以下代码将财富分配数据绘制成一系列矩形,除了最后一个柱子:

(defn draw-bars []
  (let [pc99    (vec (butlast wealth-distribution))
        pc1     (last wealth-distribution)
        y-max   (apply max pc99)
        y-scale (fn [x] (* (/ x y-max) plot-height))
        offset  (fn [i] (* (quot i 10) 7))]
    (dotimes [i 99] ;; Draw the 99%
      (let [bar-height (y-scale (nth pc99 i))]
        (q/rect (+ plot-x (* i bar-width) (offset i))
                (+ plot-y (- plot-height bar-height))
                bar-width bar-height)))
    (let [n-bars 5  ;; Draw the 1%
          bar-height (y-scale (/ pc1 n-bars))]
      (q/rect (+ plot-x (* 100 bar-width) (offset 100))
              (+ plot-y (- plot-height bar-height))
              (* bar-width n-bars) bar-height))))

已绘制的柱子代表99%的人口,最后一个柱子代表1%的人口。为了使其适合我们设计的垂直比例且不超出草图顶部,我们将该柱子相应加宽,同时保持其面积不变。因此,这个柱子比其他柱子短5倍,但宽5倍。以下是生成草图的代码:

(defn ex-10-12 []
  (let [size [960 540]]
    (q/sketch :size size
              :setup draw-bars)))

这个示例输出的图表能更清晰地显示最大柱子之间的关系,但还不太能明显看出这是一个图表。接下来,我们将添加文本以标识图表的主题和坐标轴范围。

6. 添加标题和坐标轴标签

像Incanter这样的专业可视化工具可以自动生成图表的坐标轴,但Quil没有提供这样的帮助。不过,由于我们已知柱子的宽度,实现起来并不困难。以下是添加坐标轴标签的代码:

(defn group-offset [i]
  (* (quot i 10) 7))
(defn draw-axis-labels []
  (q/fill 0)
  (q/text-align :left)
  (q/text-size 12)
  (doseq [pc (range 0 (inc 100) 10)
          :let [offset (group-offset pc)
                x      (* pc bar-width)]]
    (q/text (str pc "%") (+ plot-x x offset) label-y))
    (q/text "\"The 1%\"" pc1-label-x  pc1-label-y))

我们还可以编写一个函数来为文本生成凸版印刷风格的浮雕效果:

(defn emboss-text [text x y]
  (q/fill 255)
  (q/text text x y)
  (q/fill 100)
  (q/text text x (- y 2)))
(defn draw-title []
  (q/text-size 35)
  (q/text-leading 35)
  (q/text-align :center :top)
  (emboss-text "ACTUAL DISTRIBUTION\nOF WEALTH IN THE US"
               title-x title-y))

以下是生成包含标题和坐标轴标签的草图的代码:

(defn ex-10-13 []
  (let [size [960 540]]
    (q/sketch :size size
              :setup #((draw-bars)
                       (draw-axis-labels)
                       (draw-title)))))

这个图表是柱子高度和面积的混合,以及自定义文本可视化的结合,在标准的图表应用程序中很难实现。使用Quil,我们可以自由地将图形和数据混合在一起。

7. 用插图提高清晰度

目前的图表还比较简陋,我们可以添加图像来增加视觉吸引力。在示例项目的资源目录中有两个SVG图像文件,一个是人物图标,另一个是从维基百科获取的美国地图。

在Quil中使用SVG图像分两步。首先,使用 q/load-shape 将图像加载到内存,该函数接受一个参数:要加载的SVG文件的路径。然后,使用 q/shape 函数将图像绘制到屏幕上,该函数需要图像的x、y位置,还可以选择指定宽度和高度。如果使用基于像素的图像(如JPEG或PNG),则应使用相应的 q/load-image q/image 函数。以下是绘制图像的代码:

(defn draw-shapes []
  (let [usa    (q/load-shape "resources/us-mainland.svg")
        person (q/load-shape "resources/person.svg")
        colors [(q/color 243 195 73)
                (q/color 231 119 46)
                (q/color 77  180 180)
                (q/color 231 74  69)
                (q/color 61  76  83)]]
    (.disableStyle usa)
    (.disableStyle person)
    (q/stroke 0 50)
    (q/fill 200)
    (q/shape usa 0 0)
    (dotimes [n 99]
      (let [quintile (quot n 20)
            x (-> (* n bar-width)
                  (+ plot-x)
                  (+ (group-offset n)))]
        (q/fill (nth colors quintile))
        (q/shape person x icons-y icon-width icon-height)))
        (q/shape person
             (+ plot-x (* 100 bar-width) (group-offset 100))
             icons-y icon-width icon-height)))

在这段代码中,我们对 usa person 形状调用了 .disableStyle ,因为SVG文件可能包含嵌入式样式,如填充颜色、描边颜色或边框宽度信息,这些会影响Quil绘制形状的方式。我们希望完全控制表示方式,所以选择禁用所有样式。同时,我们只加载一次 person 形状,并使用 dotimes 多次绘制它,根据用户所在的五分位数设置颜色。以下是生成包含图像的草图的代码:

(defn ex-10-14 []
  (let [size [960 540]]
    (q/sketch :size size
              :setup #((draw-shapes)
                       (draw-bars)
                       (draw-axis-labels)
                       (draw-title)))))

现在的图表看起来更像样了,人物图标有助于传达每个柱子代表人口的一个百分位的概念。但柱子还不够吸引人,由于每个柱子代表每个人的财富,我们可以将每个柱子表示为一堆钞票。以下是绘制钞票的代码:

(defn banknotes [x y width height]
  (q/no-stroke)
  (q/fill 80 127 64)
  (doseq [y (range (* 3 (quot y 3)) (+ y height) 3)
          x (range x (+ x width) 7)]
    (q/rect x y 6 2)))

代码中需要将起始y位置调整为3的偶数倍,以确保所有钞票在偶数倍后与x轴相遇,这是从顶部到底部绘制柱子的副作用。以下是将绘制钞票函数添加到草图的代码:

(defn ex-10-15 []
  (let [size [960 540]]
    (q/sketch :size size
              :setup #((draw-shapes)
                       (draw-banknotes)
                       (draw-axis-labels)
                       (draw-title)))))

这个图表现在比较完整地展示了美国的实际财富分配情况。原YouTube视频的一个优势是将实际分配与人们期望的分配和他们理想的分配进行了对比。

8. 纳入额外数据

哈佛商学院教授Michael Norton和行为经济学家Dan Ariely对5000多名美国人进行了一项研究,以评估他们对财富分配的看法。当向他们展示各种财富分配示例并要求他们识别哪个来自美国时,大多数人选择了比实际情况更平衡的分配。当被要求选择他们理想的财富分配时,92%的人选择了更公平的分配。

研究结果如下表所示:
| 五分位数 | 理想百分比 | 期望百分比 | 实际百分比 |
| — | — | — | — |
| 100th | 32.0% | 58.5% | 84.5% |
| 80th | 22.0% | 20.0% | 11.5% |
| 60th | 21.5% | 12.0% | 3.7% |
| 40th | 14.0% | 6.5% | 0.2% |
| 20th | 10.5% | 3.0% | 0.1% |

我们可以将理想和期望的分配数据绘制到现有的财富分配图表上。为了使两个数据集具有可比性,我们可以利用这个机会学习如何在Quil中绘制复杂形状。要绘制相关形状,我们需要计算高度x、y和z。假设A、B、C区域的宽度为w,则:
- (x = \frac{A}{w})
- (y = x + \frac{B - xw}{2w})
- (z = y + \frac{C - yw}{2w})

通过这些计算,我们可以得到在图表上绘制的坐标。这样,我们就能更全面地展示不同分配情况之间的差异。

数据可视化:美国财富分配的直观呈现

9. 计算复杂形状高度以绘制额外数据

为了在现有的财富分配图表上绘制理想和期望的财富分配数据,我们需要计算相关形状的高度。根据前面提到的公式,我们可以将其转化为代码来实现这些计算。以下是具体的计算过程和代码示例:

假设我们已经有了A、B、C区域的面积值,以及宽度w,我们可以通过以下代码来计算高度x、y和z:

(defn calculate-heights [A B C w]
  (let [x (/ A w)
        y (+ x (/ (- B (* x w)) (* 2 w)))
        z (+ y (/ (- C (* y w)) (* 2 w)))]
    [x y z]))

;; 示例数据
(def A 100)
(def B 200)
(def C 300)
(def w 10)

(def [x y z] (calculate-heights A B C w))
(println "高度x:" x)
(println "高度y:" y)
(println "高度z:" z)

在上述代码中, calculate-heights 函数接受A、B、C区域的面积值和宽度w作为参数,通过公式计算出高度x、y和z,并将其作为一个向量返回。我们可以根据实际的A、B、C和w值调用这个函数,得到相应的高度值,然后使用这些高度值在图表上绘制形状。

10. 绘制理想和期望财富分配形状

在得到高度x、y和z之后,我们就可以使用Quil来绘制代表理想和期望财富分配的形状。以下是一个简单的示例代码,展示了如何使用这些高度值在图表上绘制形状:

(defn draw-ideal-expected-shapes [x y z w plot-x plot-y]
  (q/stroke 0)
  (q/fill (q/color 0 255 0)) ; 理想分配颜色,绿色
  (q/rect plot-x plot-y w x)

  (q/fill (q/color 0 0 255)) ; 期望分配颜色,蓝色
  (let [y-pos (+ plot-y x)]
    (q/rect plot-x y-pos w (- y x)))

  (let [z-pos (+ plot-y y)]
    (q/rect plot-x z-pos w (- z y))))

(defn ex-10-16 []
  (let [size [960 540]
        A 100
        B 200
        C 300
        w 10
        [x y z] (calculate-heights A B C w)
        plot-x 56
        plot-y 60]
    (q/sketch :size size
              :setup #((draw-bars)
                       (draw-axis-labels)
                       (draw-title)
                       (draw-ideal-expected-shapes x y z w plot-x plot-y)))))

在上述代码中, draw-ideal-expected-shapes 函数接受高度x、y和z,宽度w,以及绘图的起始x和y坐标作为参数。它首先绘制代表理想财富分配的矩形,然后绘制代表期望财富分配的矩形。 ex-10-16 函数则是一个完整的Quil草图示例,它调用了之前定义的绘制柱子、坐标轴标签、标题的函数,以及新的绘制理想和期望财富分配形状的函数。

11. 总结与可视化效果

通过以上步骤,我们完成了一个较为完整的美国财富分配可视化图表。这个图表不仅展示了实际的财富分配情况,还将人们理想和期望的财富分配情况也纳入其中,通过对比可以更直观地看出不同分配之间的差异。

整个可视化过程的流程图如下:

graph LR
    A[获取财富分配数据] --> B[数据预处理]
    B --> C[绘制基本柱状图]
    C --> D[调整柱子宽度和高度]
    D --> E[添加标题和坐标轴标签]
    E --> F[添加插图]
    F --> G[绘制钞票表示柱子]
    G --> H[纳入理想和期望分配数据]
    H --> I[计算形状高度]
    I --> J[绘制理想和期望分配形状]
    J --> K[完成可视化图表]

在这个过程中,我们使用了Quil这个工具,它提供了很大的灵活性,让我们能够自由地将图形和数据混合在一起,实现一些在标准图表应用程序中难以实现的效果。同时,我们也遵循了可视化的原则,制作出简单而强大的图表,以有效地向不同的受众传达信息。

以下是整个过程中涉及的主要代码和功能总结:
| 功能 | 代码示例 |
| — | — |
| 输出图像文件 | (q/save "heatmap.png") |
| 创建基本柱状图 | (c/bar-chart categories percentage :x-label "Category" :y-label "% Financial Wealth") |
| 调整财富分配数据表示 | (def wealth-distribution (concat (repeat 80 (/ 5 80)) ...)) |
| 绘制不同宽度的柱子 | (defn draw-bars [] ...) |
| 添加标题和坐标轴标签 | (defn draw-axis-labels [] ...) |
| 绘制插图 | (defn draw-shapes [] ...) |
| 绘制钞票表示柱子 | (defn banknotes [x y width height] ...) |
| 计算理想和期望分配形状高度 | (defn calculate-heights [A B C w] ...) |
| 绘制理想和期望分配形状 | (defn draw-ideal-expected-shapes [x y z w plot-x plot-y] ...) |

通过这些代码和步骤,我们可以清晰地看到美国财富分配的实际情况、人们的期望和理想状态,为进一步的分析和讨论提供了直观的依据。

基于径向基函数神经网络RBFNN的自适应滑模控制学习(Matlab代码实现)内容概要:本文介绍了基于径向基函数神经网络(RBFNN)的自适应滑模控制方法,并提供了相应的Matlab代码实现。该方法结合了RBF神经网络的非线性逼近能力和滑模控制的强鲁棒性,用于解决复杂系统的控制问题,尤其适用于存在不确定性和外部干扰的动态系统。文中详细阐述了控制算法的设计思路、RBFNN的结构与权重更新机制、滑模面的构建以及自适应律的推导过程,并通过Matlab仿真验证了所提方法的有效性和稳定性。此外,文档还列举了大量相关的科研方向和技术应用,涵盖智能优化算法、机器学习、电力系统、路径规划等多个领域,展示了该技术的广泛应用前景。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的研究生、科研人员及工程技术人员,特别是从事智能控制、非线性系统控制及相关领域的研究人员; 使用场景及目标:①学习和掌握RBF神经网络与滑模控制相结合的自适应控制策略设计方法;②应用于电机控制、机器人轨迹跟踪、电力电子系统等存在模型不确定性或外界扰动的实际控制系统中,提升控制精度与鲁棒性; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,深入理解算法实现细节,同时可参考文中提及的相关技术方向拓展研究思路,注重理论分析与仿真验证相结合。
先展示下效果 https://pan.quark.cn/s/a4b39357ea24 本项目是本人参加BAT等其他公司电话、现场面试之后总结出来的针对Java面试的知识点或真题,每个点或题目都是在面试中被问过的。 除开知识点,一定要准备好以下套路: 个人介绍,需要准备一个1分钟的介绍,包括学习经历、工作经历、项目经历、个人优势、一句话总结。 一定要自己背得滚瓜烂熟,张口就来 抽象概念,当面试官问你是如何理解多线程的时候,你要知道从定义、来源、实现、问题、优化、应用方面系统性地回答 项目强化,至少与知识点的比例是五五开,所以必须针对简历中的两个以上的项目,形成包括【架构和实现细节】,【正常流程和异常流程的处理】,【难点+坑+复盘优化】三位一体的组合拳 压力练习,面试的时候难免紧张,可能会严重影响发挥,通过平时多找机会参与交流分享,或找人做压力面试来改善 表达练习,表达能力非常影响在面试中的表现,能否简练地将答案告诉面试官,可以通过给自己讲解的方式刻意练习 重点针对,面试官会针对简历提问,所以请针对简历上写的所有技术点进行重点准备 Java基础 JVM原理 集合 多线程 IO 问题排查 Web框架、数据库 Spring MySQL Redis 通用基础 操作系统 网络通信协议 排序算法 常用设计模式 从URL到看到网页的过程 分布式 CAP理论 锁 事务 消息队列 协调器 ID生成方式 一致性hash 限流 微服务 微服务介绍 服务发现 API网关 服务容错保护 服务配置中心 算法 数组-快速排序-第k大个数 数组-对撞指针-最大蓄水 数组-滑动窗口-最小连续子数组 数组-归并排序-合并有序数组 数组-顺时针打印矩形 数组-24点游戏 链表-链表反转-链表相加 链表-...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值