Matplotlib创建时间序列螺旋图:可视化周期性数据的创新方法
参考:Creating a Temporal Range Time-Series Spiral Plot
时间序列螺旋图是一种独特而强大的数据可视化方法,特别适用于展示具有周期性模式的时间序列数据。这种图表将时间数据沿着螺旋路径展开,使得周期性模式更加直观可见。本文将详细介绍如何使用Matplotlib创建时间序列螺旋图,包括基本概念、实现步骤、以及各种自定义选项。
1. 时间序列螺旋图的基本概念
时间序列螺旋图将时间数据映射到极坐标系中的螺旋路径上。每个完整的圈代表一个周期(如一天、一周或一年),而数据值则通过颜色或点的大小来表示。这种表示方法有以下优点:
- 直观展示周期性模式
- 可以比较不同周期之间的变化
- 能够在单个图表中展示大量数据点
2. 使用Matplotlib创建基本的时间序列螺旋图
首先,让我们创建一个基本的时间序列螺旋图。我们将使用一年的每日温度数据作为示例。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成示例数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10 # 随机温度数据
# 计算角度和半径
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
# 转换为笛卡尔坐标
x = radius * np.cos(theta)
y = radius * np.sin(theta)
# 创建颜色映射
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
# 创建线段集合
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
# 创建图表
fig, ax = plt.subplots(figsize=(10, 10))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
# 添加颜色条
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Temperature Time-Series Spiral Plot\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
在这个示例中,我们首先生成了一年的每日温度数据。然后,我们将日期转换为角度和半径,并使用这些值创建螺旋形状。温度数据被映射到颜色上,使用viridis颜色映射。最后,我们使用LineCollection来绘制螺旋线,并添加了一个颜色条来显示温度范围。
3. 自定义螺旋图的外观
我们可以通过调整各种参数来自定义螺旋图的外观。以下是一些常见的自定义选项:
3.1 调整螺旋的紧密度
通过修改半径计算方式,我们可以调整螺旋的紧密度:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)**2 # 使用平方函数调整紧密度
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(10, 10))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Customized Spiral Tightness\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
在这个例子中,我们将半径计算改为radius = np.linspace(0, 1, days)**2
,这使得螺旋在中心更紧密,向外逐渐变松。
3.2 使用不同的颜色映射
Matplotlib提供了多种颜色映射,我们可以根据数据的性质选择合适的颜色映射:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.coolwarm(norm(temperatures)) # 使用coolwarm颜色映射
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(10, 10))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
sm = plt.cm.ScalarMappable(cmap='coolwarm', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Spiral Plot with Coolwarm Colormap\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
这个例子使用了’coolwarm’颜色映射,它更适合表示温度数据,因为它使用蓝色表示低温,红色表示高温。
3.3 添加日期标签
为了使图表更具信息性,我们可以在螺旋的关键位置添加日期标签:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(12, 12))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
# 添加日期标签
for i, date in enumerate(dates):
if date.day == 1: # 每月第一天添加标签
angle = i / days * 2 * np.pi
x = (1.05 + 0.05 * (i // 30)) * np.cos(angle)
y = (1.05 + 0.05 * (i // 30)) * np.sin(angle)
ax.text(x, y, date.strftime('%b'), ha='center', va='center', rotation=angle*180/np.pi-90)
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Spiral Plot with Date Labels\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
这个例子在螺旋的外围添加了月份标签,使得观察者可以更容易地定位时间。
4. 处理多年数据
时间序列螺旋图也可以用来展示跨越多年的数据。以下是一个展示3年温度数据的例子:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成3年的数据
years = 3
days = 365 * years
start_date = datetime.datetime(2021, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
# 计算角度和半径
theta = np.linspace(0, 2*np.pi*years, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(12, 12))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
# 添加年份标签
for year in range(years):
angle = year * 2 * np.pi
x = 1.05 * np.cos(angle)
y = 1.05 * np.sin(angle)
ax.text(x, y, str(2021 + year), ha='center', va='center', fontsize=12, fontweight='bold')
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Multi-Year Temperature Spiral Plot\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
这个例子展示了如何创建一个跨越多年的螺旋图,并在适当的位置添加年份标签。
5. 添加交互性
为了增强用户体验,我们可以为螺旋图添加一些交互性功能。以下是一个使用鼠标悬停显示具体数据的例子:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
class InteractiveSpiralPlot:
def __init__(self, master):
self.master = master
self.master.title("Interactive Spiral Plot - how2matplotlib.com")
# 生成数据
self.days = 365
self.start_date = datetime.datetime(2023, 1, 1)
self.dates = [self.start_date + datetime.timedelta(days=i) for i in range(self.days)]
self.temperatures = np.random.rand(self.days) * 30 + 10
# 创建图表
self.fig, self.ax = plt.subplots(figsize=(10, 10))
self.canvas = FigureCanvasTkAgg(self.fig, master=self.master)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.plot_spiral()
# 添加鼠标事件
self.canvas.mpl_connect('motion_notify_event', self.on_hover)
# 添加文本显示
self.text = self.ax.text(0.05, 0.95, '', transform=self.ax.transAxes, va='top')
def plot_spiral(self):
theta = np.linspace(0, 2*np.pi, self.days)
radius = np.linspace(0, 1, self.days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=self.temperatures.min(), vmax=self.temperatures.max())
colors = plt.cm.viridis(norm(self.temperatures))
points = np.array[x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
self.lc = LineCollection(segments, colors=colors, linewidth=2)
self.ax.add_collection(self.lc)
self.ax.set_xlim(-1.1, 1.1)
self.ax.set_ylim(-1.1, 1.1)
self.ax.set_aspect('equal')
self.ax.axis('off')
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
self.fig.colorbar(sm, ax=self.ax, label='Temperature (°C)')
self.ax.set_title('Interactive Temperature Spiral Plot\nhow2matplotlib.com', fontsize=16)
def on_hover(self, event):
if event.inaxes == self.ax:
x, y = event.xdata, event.ydata
dist = np.sqrt((x - self.lc.get_segments()[:, 0, 0])**2 +
(y - self.lc.get_segments()[:, 0, 1])**2)
closest_point = np.argmin(dist)
date = self.dates[closest_point]
temp = self.temperatures[closest_point]
self.text.set_text(f'Date: {date.strftime("%Y-%m-%d")}\nTemperature: {temp:.2f}°C')
self.canvas.draw_idle()
if __name__ == "__main__":
root = tk.Tk()
app = InteractiveSpiralPlot(root)
root.mainloop()
这个示例创建了一个交互式的螺旋图,当用户将鼠标悬停在图表上时,会显示最接近的数据点的日期和温度信息。这种交互性大大增强了用户体验,使得数据探索更加直观和有趣。
6. 处理缺失数据
在实际应用中,我们经常会遇到数据缺失的情况。以下是一个处理缺失数据的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize, LinearSegmentedColormap
import datetime
# 生成带有缺失值的数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
missing_indices = np.random.choice(days, size=30, replace=False)
temperatures[missing_indices] = np.nan
# 创建自定义颜色映射,将缺失值显示为灰色
colors = ['gray'] + plt.cm.viridis(np.linspace(0, 1, 256)).tolist()
n_bins = 257 # 256 for viridis + 1 for gray
cmap = LinearSegmentedColormap.from_list('custom_viridis', colors, N=n_bins)
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=np.nanmin(temperatures), vmax=np.nanmax(temperatures))
colors = cmap(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(12, 12))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
cbar.set_ticks(list(cbar.get_ticks()) + [np.nanmin(temperatures) - 1])
cbar.set_ticklabels(list(cbar.get_ticklabels()) + ['Missing'])
plt.title('Spiral Plot with Missing Data\nhow2matplotlib.com', fontsize=16)
plt.show()
在这个例子中,我们随机选择了30个数据点设为缺失值(NaN)。我们创建了一个自定义的颜色映射,将缺失值显示为灰色,而其他数据点仍然使用viridis颜色映射。这样,缺失的数据点在图表中清晰可见,不会被忽视。
7. 添加参考线和注释
为了使螺旋图更具信息性,我们可以添加一些参考线和注释。以下是一个示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
fig, ax = plt.subplots(figsize=(12, 12))
ax.add_collection(lc)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
# 添加季节分隔线
for i in range(4):
angle = i * np.pi / 2
ax.plot([0, np.cos(angle)], [0, np.sin(angle)], 'k--', alpha=0.5)
# 添加季节标签
seasons = ['Spring', 'Summer', 'Autumn', 'Winter']
for i, season in enumerate(seasons):
angle = (i * np.pi / 2) + np.pi / 4
x = 1.15 * np.cos(angle)
y = 1.15 * np.sin(angle)
ax.text(x, y, season, ha='center', va='center', fontweight='bold')
# 添加温度阈值线
threshold = 25
mask = temperatures > threshold
ax.scatter(x[mask], y[mask], c='red', s=10, alpha=0.5)
# 添加注释
ax.annotate('High temperature\nthreshold (25°C)', xy=(0.7, 0.7), xytext=(0.9, 0.9),
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='right', verticalalignment='top')
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Annotated Temperature Spiral Plot\nhow2matplotlib.com', fontsize=16)
plt.show()
在这个例子中,我们添加了以下元素:
1. 季节分隔线:使用虚线将螺旋图分为四个季节。
2. 季节标签:在适当的位置添加春、夏、秋、冬的标签。
3. 温度阈值线:标记出超过某个温度阈值(这里是25°C)的点。
4. 注释:为高温阈值添加了一个带箭头的注释。
这些额外的元素使得螺旋图更加丰富和信息化,有助于观察者更好地理解和解释数据。
8. 比较多个数据集
时间序列螺旋图也可以用来比较多个相关的数据集。以下是一个比较两年温度数据的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
def create_spiral(temperatures, start_date):
days = len(temperatures)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
return x, y, dates
# 生成两年的数据
days = 365
start_date_2022 = datetime.datetime(2022, 1, 1)
start_date_2023 = datetime.datetime(2023, 1, 1)
temperatures_2022 = np.random.rand(days) * 30 + 10
temperatures_2023 = np.random.rand(days) * 30 + 10
x_2022, y_2022, dates_2022 = create_spiral(temperatures_2022, start_date_2022)
x_2023, y_2023, dates_2023 = create_spiral(temperatures_2023, start_date_2023)
# 创建图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
# 2022年螺旋图
norm_2022 = Normalize(vmin=temperatures_2022.min(), vmax=temperatures_2022.max())
colors_2022 = plt.cm.viridis(norm_2022(temperatures_2022))
points_2022 = np.array([x_2022, y_2022]).T.reshape(-1, 1, 2)
segments_2022 = np.concatenate([points_2022[:-1], points_2022[1:]], axis=1)
lc_2022 = LineCollection(segments_2022, colors=colors_2022, linewidth=2)
ax1.add_collection(lc_2022)
ax1.set_xlim(-1.1, 1.1)
ax1.set_ylim(-1.1, 1.1)
ax1.set_aspect('equal')
ax1.axis('off')
ax1.set_title('Temperature 2022\nhow2matplotlib.com', fontsize=16)
sm_2022 = plt.cm.ScalarMappable(cmap='viridis', norm=norm_2022)
sm_2022.set_array([])
plt.colorbar(sm_2022, ax=ax1, label='Temperature (°C)')
# 2023年螺旋图
norm_2023 = Normalize(vmin=temperatures_2023.min(), vmax=temperatures_2023.max())
colors_2023 = plt.cm.viridis(norm_2023(temperatures_2023))
points_2023 = np.array([x_2023, y_2023]).T.reshape(-1, 1, 2)
segments_2023 = np.concatenate([points_2023[:-1], points_2023[1:]], axis=1)
lc_2023 = LineCollection(segments_2023, colors=colors_2023, linewidth=2)
ax2.add_collection(lc_2023)
ax2.set_xlim(-1.1, 1.1)
ax2.set_ylim(-1.1, 1.1)
ax2.set_aspect('equal')
ax2.axis('off')
ax2.set_title('Temperature 2023\nhow2matplotlib.com', fontsize=16)
sm_2023 = plt.cm.ScalarMappable(cmap='viridis', norm=norm_2023)
sm_2023.set_array([])
plt.colorbar(sm_2023, ax=ax2, label='Temperature (°C)')
plt.tight_layout()
plt.show()
Output:
这个例子创建了两个并排的螺旋图,分别显示2022年和2023年的温度数据。这种并排比较可以帮助观察者轻松识别两年之间的温度模式差异。
9. 动画效果
为了使时间序列螺旋图更具吸引力,我们可以添加动画效果。以下是一个创建动画螺旋图的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
# 创建图表
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_aspect('equal')
ax.axis('off')
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
plt.colorbar(sm, ax=ax, label='Temperature (°C)')
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
theta = np.linspace(0, 2*np.pi*i/days, i)
radius = np.linspace(0, 1, i)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
colors = plt.cm.viridis(norm(temperatures[:i]))
line.set_color(colors)
line.set_data(x, y)
ax.set_title(f'Temperature Spiral Plot\n{dates[i].strftime("%Y-%m-%d")}\nhow2matplotlib.com', fontsize=16)
return line,
anim = FuncAnimation(fig, animate, init_func=init, frames=days, interval=20, blit=True)
plt.show()
Output:
这个示例创建了一个动画效果,展示螺旋图随时间逐渐展开的过程。每一帧都会更新日期标题,使观察者能够清楚地看到数据是如何随时间变化的。
10. 多变量螺旋图
时间序列螺旋图也可以用来展示多个变量。以下是一个同时展示温度和湿度的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
humidity = np.random.rand(days) * 60 + 40
# 计算角度和半径
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
# 创建图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
# 温度螺旋图
norm_temp = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors_temp = plt.cm.viridis(norm_temp(temperatures))
points_temp = np.array([x, y]).T.reshape(-1, 1, 2)
segments_temp = np.concatenate([points_temp[:-1], points_temp[1:]], axis=1)
lc_temp = LineCollection(segments_temp, colors=colors_temp, linewidth=2)
ax1.add_collection(lc_temp)
ax1.set_xlim(-1.1, 1.1)
ax1.set_ylim(-1.1, 1.1)
ax1.set_aspect('equal')
ax1.axis('off')
ax1.set_title('Temperature Spiral\nhow2matplotlib.com', fontsize=16)
sm_temp = plt.cm.ScalarMappable(cmap='viridis', norm=norm_temp)
sm_temp.set_array([])
plt.colorbar(sm_temp, ax=ax1, label='Temperature (°C)')
# 湿度螺旋图
norm_hum = Normalize(vmin=humidity.min(), vmax=humidity.max())
colors_hum = plt.cm.Blues(norm_hum(humidity))
points_hum = np.array([x, y]).T.reshape(-1, 1, 2)
segments_hum = np.concatenate([points_hum[:-1], points_hum[1:]], axis=1)
lc_hum = LineCollection(segments_hum, colors=colors_hum, linewidth=2)
ax2.add_collection(lc_hum)
ax2.set_xlim(-1.1, 1.1)
ax2.set_ylim(-1.1, 1.1)
ax2.set_aspect('equal')
ax2.axis('off')
ax2.set_title('Humidity Spiral\nhow2matplotlib.com', fontsize=16)
sm_hum = plt.cm.ScalarMappable(cmap='Blues', norm=norm_hum)
sm_hum.set_array([])
plt.colorbar(sm_hum, ax=ax2, label='Humidity (%)')
plt.tight_layout()
plt.show()
Output:
这个例子创建了两个并排的螺旋图,一个显示温度数据,另一个显示湿度数据。这种方法允许我们在同一时间范围内比较不同的变量。
11. 自定义螺旋形状
我们还可以通过修改半径计算方式来自定义螺旋的形状。以下是一个创建对数螺旋的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
# 计算角度和半径(对数螺旋)
theta = np.linspace(0, 4*np.pi, days)
a, b = 0.1, 0.1 # 控制螺旋的形状
radius = a * np.exp(b * theta)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
# 创建图表
fig, ax = plt.subplots(figsize=(12, 12))
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
ax.add_collection(lc)
ax.set_xlim(x.min()-0.1, x.max()+0.1)
ax.set_ylim(y.min()-0.1, y.max()+0.1)
ax.set_aspect('equal')
ax.axis('off')
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
plt.colorbar(sm, ax=ax, label='Temperature (°C)')
plt.title('Logarithmic Spiral Temperature Plot\nhow2matplotlib.com', fontsize=16)
plt.show()
Output:
这个例子使用对数函数来计算螺旋的半径,resulting in a logarithmic spiral shape. 这种形状可以在某些情况下更好地展示数据的变化趋势。
12. 结合其他图表类型
我们可以将时间序列螺旋图与其他类型的图表结合,以提供更全面的数据视图。以下是一个将螺旋图与传统的时间序列线图结合的示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import Normalize
import datetime
# 生成数据
days = 365
start_date = datetime.datetime(2023, 1, 1)
dates = [start_date + datetime.timedelta(days=i) for i in range(days)]
temperatures = np.random.rand(days) * 30 + 10
# 创建图表
fig = plt.figure(figsize=(20, 10))
gs = fig.add_gridspec(1, 2)
# 螺旋图
ax1 = fig.add_subplot(gs[0, 0], projection='polar')
theta = np.linspace(0, 2*np.pi, days)
radius = np.linspace(0, 1, days)
norm = Normalize(vmin=temperatures.min(), vmax=temperatures.max())
colors = plt.cm.viridis(norm(temperatures))
points = np.array([theta, radius]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, colors=colors, linewidth=2)
ax1.add_collection(lc)
ax1.set_ylim(0, 1)
ax1.set_yticks([])
ax1.set_xticks(np.linspace(0, 2*np.pi, 12, endpoint=False))
ax1.set_xticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
ax1.set_title('Temperature Spiral\nhow2matplotlib.com', fontsize=16)
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
plt.colorbar(sm, ax=ax1, label='Temperature (°C)')
# 时间序列线图
ax2 = fig.add_subplot(gs[0, 1])
ax2.plot(dates, temperatures)
ax2.set_xlabel('Date')
ax2.set_ylabel('Temperature (°C)')
ax2.set_title('Temperature Time Series\nhow2matplotlib.com', fontsize=16)
plt.tight_layout()
plt.show()
Output:
这个例子创建了一个包含两个子图的图表:左侧是温度数据的螺旋图,右侧是相同数据的传统时间序列线图。这种组合可以同时展示数据的周期性模式和长期趋势。
结论
时间序列螺旋图是一种强大而灵活的数据可视化工具,特别适合展示具有周期性模式的时间序列数据。通过使用Matplotlib,我们可以创建各种自定义的螺旋图,包括基本的单变量螺旋图、多变量比较、动画效果,以及与其他图表类型的结合。
这种可视化方法的主要优势在于它能够在单个图表中同时展示长期趋势和周期性模式,使得数据的模式和异常更容易被识别。然而,它也有一些限制,例如对于非周期性数据可能不太适用,并且在处理大量数据时可能会变得视觉上复杂。
在实际应用中,时间序列螺旋图可以用于各种领域,如气象数据分析、股票市场趋势可视化、能源消耗模式研究等。通过适当的自定义和结合其他可视化技术,我们可以创建出既美观又信息丰富的数据可视化作品。
最后,重要的是要记住,好的数据可视化不仅仅是创建漂亮的图表,更重要的是要有效地传达数据中的信息和洞察。因此,在使用时间序列螺旋图或任何其他可视化技术时,都应该始终考虑目标受众和要传达的关键信息。