一图胜千言!深度掌握 Python 绘图

2024年 5月 28日 75.0k 0

据可视化是数据科学家传达洞见和讲述数据故事的关键工具。作为 Python 开发者,我们拥有丰富的可视化库和工具,能够创建各种引人入胜的图表。本文将探索一些鲜为人知但实用的可视化类型,如桑基图(Sankey Diagrams)、脊线图(Ridge Plots)、内嵌图(Insets)、雷达图(Radar Chart)和词云图(Word Cloud Plots)。我们将主要使用流行的 Matplotlib、Seaborn 和 Plotly 这些 Python 库来实现这些有趣的可视化效果,让你的数据讲述更加生动有趣。

我们将使用形状、大小、颜色、方向、面积和标记符号等属性,为十种不同的使用案例创建绘图。在每个使用案例中,我们的目标都是创建有效、高效和美观的可视化效果。描述这些词语在图表中的含义如下:

(a) 有效:图表中包含了所有需要传达的信息(b) 高效:图表中没有多余的数据(c) 美观:图表以清晰的方式展示数据,吸引注意力

所有图表都是二维图,因为从效率和效果的角度来看,二维图比三维图更清晰易懂,同时更容易描绘距离。文中还将介绍每个用例的代码,并讨论代码和图表中的要点。

用例 1

描述大学之间学生交流流动的桑基图。

这些图展示了资源流动的情况,下面的代码展示了用例的实现。 字符 "A" 代表第一所大学,字符 "B" 代表第二所大学。 数字 3、4、5 分别代表不同的系,即{Statistics, Math, Physics}。第 25 行创建了一个图表,其中 node 和 link是字典。 node 使用的 label 对象由唯一的 Depts 院系组成,而 link 使用的两个列表分别由 sending"院系的索引和 acepting 院系的索引组成。

import pandas as pd
import plotly.graph_objects as gr
data = {
    'Sending_Dept':   ['5A', '4A', '5B', '5A', '4B', '4A', '3A', '3B', '3A', '3B', '3A', '3B'],
    'Accepting_Dept': ['4B', '5B', '5A', '5B', '4A', '4B', '5B', '5A', '4B', '4A', '3B', '3A'],
    'FlowValue': [1, 3, 4, 3, 4, 4, 1, 1, 3, 2, 5, 3]
}
df = pd.DataFrame(data)
unique_departments = set(df['Sending_Dept']).union(set(df['Accepting_Dept']))
Depts = list(unique_departments)
Dept_indices = {}
for i, dept in enumerate(Depts):
    Dept_indices[dept] = i
sending_indices = []
for dept in df['Sending_Dept']:
    dept_index = Dept_indices[dept]
    sending_indices.append(dept_index)
    print(f"Sending indices are: {sending_indices}")
accepting_indices = []
for dept in df['Accepting_Dept']:
    dept_index = Dept_indices[dept]
    accepting_indices.append(dept_index)
flowvalues = df['FlowValue'].tolist()
# Sankey diagram
fig = gr.Figure(data=[gr.Sankey(
    node=dict( pad=10,thickness=25,line=dict(color="red", width=0.8),label=Depts,),
    link=dict(source=sending_indices,target=accepting_indices,value=flowvalues
    ))])
fig.update_layout(title_text="Sankey Diagram of exchange students flow between University Depts", font_size=12)
fig.show()

生成的"桑基图"图(1)中,节点3A旁的橙色矩形显示了光标放置在节点上时的情况。当光标位于节点"3A"上时,我们可以看到A大学3系接受和派遣交换生的频率。它接受学生1次,派遣学生3次。我们还可以从上面代码片段中的 data 字典推断出这一点,因为"3A"在Sending_Dept列表中出现了3次,在Accepting_Dept列表中出现了1次。节点 "3A" 左边的数字9是它向B大学派出的交换生总数。我们还可以通过在Sending_Dept列表中添加与3A相对应的FlowValues来推断。

我们还注意到,当我们点击节点 "3A" 时,从它发出的箭头会变暗,并显示出与 "3A" 交换学生的其他节点。箭头的粗细与 FlowValues 相对应。总之,桑基图利用箭头的方向和粗细来传递流动信息,并以文字为基础为每个节点形成累积流动。

一图胜千言!深度掌握 Python 绘图-1图 1. 桑基图显示了两所大学各系之间的学生交流流

用例 2

绘制一家房地产中介公司的房屋销售数据。

一位数据科学家在房地产中介公司工作,机构要求绘制上个月售出房屋信息的二维图。每栋售出的房屋需包含房价、距离市中心、方向、代理佣金和销售代理的公司级别(助理、副总裁、合伙人)的信息。二维图形信息量大,可使用复杂对象描述地块上的每栋房屋。具体来说,使用“笑脸表情符号”实现方法的代码片段如下。

import matplotlib.pyplot as plt
import numpy as np
np.random.seed(125)
num_houses = 10
distances = np.random.uniform(0, 30, num_houses)  # distance from city center
prices = np.random.uniform(400, 2000, num_houses) * 1000  # sale price in thousands
directions = np.random.choice(['N', 'S', 'E', 'W'], num_houses)  # direction from city center
agent_levels = np.random.choice([1, 2, 3], num_houses)  # agent's level
def get_emoji_size(level):
    size_map = {1: 250, 2: 380, 3: 700}
    return size_map.get(level, 120)  # Increased size for better visibility
def get_emoji_color_new(price):
    if price < 600000:
        return 'white'  # Light yellow for $400k-$600k
    elif price < 800000:
        return 'yellow'        # White for $600k-$800k
    elif price < 1000000:
        return 'pink'         # Pink for $800k-$1 million
    else:
        return 'lime'         # Lime for $1 million-$2 million
def rotate_smiley(direction):
    rotation_map = {'N': 0, 'E': 270, 'S': 180, 'W': 90}
    return rotation_map.get(direction, 0)  # default no rotation if direction not found
plt.figure(figsize=(12, 8))
for i in range(num_houses):
    plt.scatter(distances[i], prices[i], s=get_emoji_size(agent_levels[i]),\
    c=get_emoji_color_new(prices[i]), 
                marker='o', edgecolors='black', alpha=0.8)
    plt.text(distances[i], prices[i], "😊", fnotallow=agent_levels[i]*10, 
             rotatinotallow=rotate_smiley(directions[i]), ha='center', va='center',\
             fnotallow='bold')
plt.xlabel('Distance from City Center (km)')
plt.ylabel('Sale Price ($)')
plt.title('House Sales Data for 10 Houses: Price vs Distance with New Color Scheme')
plt.grid(True)
plt.show()

如上面的代码和下面图 2 中的散点图所示,X 轴和 Y 轴分别对应于与市中心的距离和销售价格。以下是一些要点:

  • 表情符号的大小用于描述销售代理的级别。尺寸越大,代理人的级别越高
  • 表情符号微笑的位置(顶部、底部、左侧、右侧)表示从市中心出发的方向。例如,如果微笑在顶部,房子就在市中心的北面。
  • 表情符号的颜色表示中介的佣金。例如,石灰色的佣金为 6%,粉红色的佣金为 5%(房地产中介的佣金政策是,售价越高,佣金越高)。

作为本使用案例的结论,我们使用散点图和表情符号的形状、颜色和大小,在二维图中表达了已售房屋的五个属性。使用复杂的对象(如笑脸)来表示数据点,确实有助于在图中包含大量信息。

一图胜千言!深度掌握 Python 绘图-2图 2. 房屋销售数据散点图

用例 3.在旭日图中显示某大学各学院和系的组成。还必须表达各学院和部门的规模信息。

这是一种不同组成部分具有层次结构的情况。在这种情况下,旭日图是理想的选择。下面的代码片段展示了实现过程。第一个数组 labels 包含旭日图的名称。第二个数组 parents 包含层次结构,而数组 values 包含段的大小。

import plotly.graph_objects as go
labels = ["PineForest University", 
          "College of Engineering", "Natural Sciences", "Medical School",
          "Chemical Engineering", "Mechanical Engineering", "Computer Science", "Electrical Engineering",\
          "Biomedical Engineering",
          "Astronomy", "Biology", "Mathematics", "Physics","Chemistry","Statistics","EarthSciences",\
          "Emergency Medicine", "Neurology", "Cardiology", "Pediatrics", "Pathology","Family Medicine", \
          "Orthopedics", "Obstetrics", "Anesthesiology", "Anatomy", "Physiology",\
          "Microbiology","Immunology"]
parents = ["", 
           "PineForest University", "PineForest University", "PineForest University",
           "College of Engineering", "College of Engineering", "College of Engineering",\
           "College of Engineering","College of Engineering",
           "Natural Sciences","Natural Sciences","Natural Sciences","Natural Sciences","Natural Sciences",\
           "Natural Sciences","Natural Sciences",
           "Medical School", "Medical School", "Medical School", "Medical School","Medical School", \
           "Medical School","Medical School", "Medical School", "Medical School",\
           "Medical School", "Medical School", "Medical School","Medical School"]
values = [0, 
          50, 30, 200, 
          85, 100, 180, 163,120,
          90,70 ,104,54,180,100,70,
          70,180,200, 85,170,89,75,120,76,150,67,56,239]
fig = go.Figure(go.Sunburst(
    labels=labels,
    parents=parents,
    values=values,
))
fig.update_layout(margin=dict(t=0, l=0, r=0, b=0))
fig.show()

图 3 显示了上述算法的输出结果。请注意,每个片段的大小与 "值 "数组中的相应数字成正比。下图显示,当我们点击一个片段时,它的大小就会显示出来(Immunology 239)。

旭日图使用大小、颜色和文本来描述大学不同实体的层次结构。

一图胜千言!深度掌握 Python 绘图-3图 3. 旭日图显示了一所大学不同学院和系的结构

用例 4

我们的房地产客户需要一张二维图,显示上个月售出房屋的信息: (a) 售价,(b) 面积,(c) 海边距离,(d) 火车站距离。必须放大已售出最多房屋的图段。

本用例与用例 2 类似,但数据科学家有新任务:创建一个放大版的最繁忙图段。这就是插图。插图可以放大图表的重要部分,提高效果。下面的代码片段显示了插图的实现。

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(0)
size_of_house = np.random.uniform(1000, 4000, 100) # Size in square feet
price = size_of_house * np.random.uniform(150, 350) + np.random.normal(0, 50000, 100) # Price
distance_from_train = np.random.uniform(0.5, 5, 100) # Distance from train in miles
distance_from_ocean = np.random.uniform(0.1, 10, 100) # Distance from ocean in miles
df = pd.DataFrame({
'Size of House': size_of_house,
'Price': price,
'Distance from Train': distance_from_train,
'Distance from Ocean': distance_from_ocean
})
# Sample 10 points for a less cluttered plot
sampled_df = df.sample(10)
# Adding the inset
# Filtering sampled data for the inset plot
#inset_data = sampled_df[(sampled_df['Size of House'] >= 2000) & (sampled_df['Size of House'] = 250000) & (sampled_df['Price']

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论