老码农和你一起学AI:Python系列-Seaborn的数据故事化

Seaborn 就像一位技艺精湛的翻译,能将杂乱的数据集转化为引人入胜的视觉叙事。今天我们深入一下这门关于数据故事化的技艺,当然需要我们既懂代码的逻辑,又通人类的情感 —— 毕竟,最好的数据故事,从来都是理性与感性的完美交融。

一、时序数据

清晨的咖啡馆里,笔记本屏幕上跳动着十年间的气温数据。我敲下rolling(window=30)的瞬间,仿佛听见时间河流里泛起的涟漪。时序数据最动人的特质,在于它藏着时间的秘密,而滚动均值可视化正是揭开这层面纱的温柔手段。

用 Seaborn 绘制滚动均值曲线时,那些突兀的峰值被柔化,隐藏的趋势逐渐显形。就像给数据装上慢镜头,让季节性波动从随机噪声中浮出水面。我曾见过一组共享单车骑行数据,原始曲线像被狂风扰乱的心电图,30 天滚动均值却清晰勾勒出春秋两季的出行高峰 —— 原来城市的脉搏,早被数据悄悄记录。

绘制的秘诀在于平衡窗口大小与时间粒度。太短的窗口保留太多杂音,太长则会模糊关键转折。当seaborn.lineplot()将原始数据与滚动曲线同时呈现时,就像在展示历史的两种面貌:一个是充满偶然的日常,一个是蕴含必然的趋势。那些交叉的节点尤其耐人寻味,仿佛是历史在对我们眨眼睛。

1、时序数据


import pandas as pd

import seaborn as sns

import matplotlib.pyplot as plt

import numpy as np

# 生成示例数据(十年气温数据)

np.random.seed(42)

dates = pd.date_range(start='2013-01-01', end='2023-12-31', freq='D')

temp = np.random.normal(loc=15, scale=8, size=len(dates)) # 基础温度

# 添加季节性波动

temp += 10 * np.sin(np.linspace(0, 10*2*np.pi, len(dates)))

df = pd.DataFrame({'date': dates, 'temperature': temp})

# 计算滚动均值

df['rolling_mean_30'] = df['temperature'].rolling(window=30).mean()

df['rolling_mean_90'] = df['temperature'].rolling(window=90).mean()

# 设置绘图风格

sns.set_style("whitegrid")

plt.figure(figsize=(12, 6))

# 绘制原始数据和滚动均值

sns.lineplot(data=df, x='date', y='temperature', color='lightgray', label='每日气温', alpha=0.6)

sns.lineplot(data=df, x='date', y='rolling_mean_30', color='#3498db', label='30天滚动均值')

sns.lineplot(data=df, x='date', y='rolling_mean_90', color='#e74c3c', label='90天滚动均值')

# 美化图表

plt.title('十年气温变化趋势与滚动均值', fontsize=16)

plt.xlabel('日期', fontsize=12)

plt.ylabel('气温 (°C)', fontsize=12)

plt.xticks(rotation=45)

plt.legend()

plt.tight_layout()

plt.show()

这段代码模拟了十年气温数据,通过不同窗口大小的滚动均值计算,展示了如何用 Seaborn 区分原始数据的噪声和潜在趋势。30 天窗口适合观察短期波动,90 天窗口则更能体现长期趋势。

二、地理数据

把数据点安放在地球表面的瞬间,它们便有了生命。当 Seaborn 的色彩美学遇上 Basemap 的地理骨架,每一个数据标记都成了讲述地域故事的标点符号。我至今记得第一次将全国 PM2.5 数据投射到中国地图上的震撼 —— 那些由绿到红的渐变,活生生是一幅环境变迁的油画。

整合 Basemap 的过程像在搭建数据的舞台。先用Basemap()勾勒出山川湖海的轮廓,再让 Seaborn 的scatterplot()在上面点缀数据的星光。最妙的是用色彩编码表达第三维度,比如用蓝到紫的渐变显示各城市的 GDP 增速,当鼠标划过那些闪烁的光点,仿佛能听见不同地域的发展节奏。

有次处理快递物流数据,我们在地图上用流动的线条展示货物运输路径。Seaborn 的调色板让繁忙程度一目了然:京广线上的深红线条像奔流的血液,而西部那些浅灰轨迹则如细流般静谧。这种可视化瞬间让决策者明白:物流网络的失衡,早被数据写在了祖国的版图上。

1、Basemap 基础整合代码


import pandas as pd

import seaborn as sns

import matplotlib.pyplot as plt

from mpl_toolkits.basemap import Basemap

import numpy as np

# 生成示例数据(中国主要城市PM2.5数据)

cities = {

'北京': (39.9042, 116.4074, 85),

'上海': (31.2304, 121.4737, 62),

'广州': (23.1291, 113.2644, 58),

'成都': (30.5723, 104.0665, 72),

'西安': (34.3416, 108.9398, 90),

'哈尔滨': (45.8038, 126.5348, 55),

'乌鲁木齐': (43.8256, 87.6168, 102)

}

df = pd.DataFrame([

{'city': city, 'lat': coord[0], 'lon': coord[1], 'pm25': coord[2]}

for city, coord in cities.items()

])

# 创建地图

plt.figure(figsize=(12, 8))

m = Basemap(

llcrnrlon=73, llcrnrlat=18,

urcrnrlon=135, urcrnrlat=53,

projection='lcc', lat_1=33, lat_2=45, lon_0=100

)

m.drawcoastlines(linewidth=0.5)

m.drawcountries(linewidth=1)

m.drawprovinces(linewidth=0.8, linestyle=':') # 绘制省份轮廓

# 转换经纬度为地图坐标

x, y = m(df['lon'].values, df['lat'].values)

# 绘制散点图

sc = plt.scatter(

x, y, c=df['pm25'], s=df['pm25']*2,

cmap='Reds', alpha=0.7, edgecolors='k'

)

# 添加颜色条和标注

cbar = plt.colorbar(sc)

cbar.set_label('PM2.5浓度')

for i, row in df.iterrows():

plt.text(x[i], y[i], row['city'], fontsize=10)

plt.title('中国主要城市PM2.5浓度分布', fontsize=16)

plt.tight_layout()

plt.show()

这段代码演示了如何将 Seaborn 的色彩映射与 Basemap 结合,用散点大小和颜色同时表达 PM2.5 的数值特征。在实际应用中,你可以根据需要替换为真实的地理数据集,如物流网点、人口分布等。

三、动态图表

当静态图表开始流动,数据便有了叙事的节奏。FuncAnimation 就像给数据装上了放映机,让我们能看见故事的起承转合。我曾用它制作过一段展示城市扩张的动画:从 1990 年的几簇光点,到 2020 年连绵成片的城市群,20 秒的光影流转里,藏着一个时代的变迁。

制作动态图表的过程像在编排一场舞蹈。先定义好每帧画面的 "舞姿"—— 可能是更新数据范围,或是变换色彩映射;再用FuncAnimation()设定好节奏,让 Seaborn 的绘图函数随时间轴舞动。最关键的转场设计,比如在数据突变处加入定格或慢放,就像电影里的特写镜头,总能抓住观众的呼吸。

记得为一家新能源车企制作过电池技术演进动画。当历年能量密度数据点在图表上跳跃上升,当突破关键阈值时用 Seaborn 的高亮效果强调,整个会议室的人都看入了迷。技术总监后来告诉我:"那些跳动的曲线,比任何报告都更能说明我们走过的路。"

1、FuncAnimation 入门代码


import pandas as pd

import seaborn as sns

import matplotlib.pyplot as plt

from matplotlib.animation import FuncAnimation

import numpy as np

# 生成示例数据(城市扩张模拟)

years = range(1990, 2021)

np.random.seed(42)

# 生成逐年增长的城市区域数据

data = []

for year in years:

# 城市中心坐标

center_x, center_y = 100, 100

# 随年份增长的城市范围

radius = 5 + (year - 1990) * 0.8

# 生成该年份的城市区域点

n_points = 50 + (year - 1990) * 10

angles = np.random.uniform(0, 2*np.pi, n_points)

dists = np.random.uniform(0, radius, n_points)

x = center_x + dists * np.cos(angles)

y = center_y + dists * np.sin(angles)

data.extend([{'year': year, 'x': xi, 'y': yi} for xi, yi in zip(x, y)])

df = pd.DataFrame(data)

# 设置绘图

sns.set_style("white")

fig, ax = plt.subplots(figsize=(10, 10))

ax.set_xlim(70, 130)

ax.set_ylim(70, 130)

scat = ax.scatter([], [], alpha=0.6, s=10)

title = ax.set_title('', fontsize=16)

# 初始化函数

def init():

scat.set_offsets([])

return scat,

# 更新函数

def update(year):

year_data = df[df['year'] == year]

scat.set_offsets(np.c_[year_data['x'], year_data['y']])

scat.set_color(sns.color_palette("viridis", as_cmap=True)(year/2020))

title.set_text(f'城市扩张模拟:{year}年')

return scat, title

# 创建动画

ani = FuncAnimation(

fig, update, frames=years,

init_func=init, blit=True, interval=300

)

# 保存动画(如需保存可取消注释)

# ani.save('city_expansion.gif', writer='pillow')

plt.show()

这段代码创建了一个模拟城市扩张的动态图表,每年的数据点会以不同颜色显示,直观展示城市区域随时间的变化。你可以调整 interval 参数控制动画速度,或修改数据生成逻辑以适应实际需求,如人口增长、森林覆盖变化等场景。

最后小结

数据故事化的真谛,在于让数字学会说话。Seaborn 提供的不只是绘图工具,更是一套叙事语法 —— 当我们掌握了滚动均值的节奏、地理映射的视角、动态变化的韵律,就能让冰冷的数据绽放出人文的温度。毕竟,最好的数据分析,从来都不是为了证明什么,而是为了讲述那些藏在数字背后的,关于我们这个世界的真实故事,未完待续........

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值