英文:
How to format the x-axis of a heat map in matplotlib?
问题
I can help you translate the code-related portion. Here's the translated code:
我正在尝试自定义x轴上的标签/刻度,使其看起来像我之前创建的另一个图表:
import matplotlib as mplt
from matplotlib import dates, pyplot
from matplotlib.transforms import ScaledTranslation
import numpy as np
import pandas as pd
import datetime
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 类别
dates_ = pd.date_range('2023/01/01', '2023/01/08', freq='3H', tz='utc') # 创建日期时间索引的列表。
matrix = pd.DataFrame(columns=s_names, index=dates_) # 初始化数据框。
matrix = matrix.applymap(lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))) # 用随机数替换数据框中的nan值。
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True) # 按第一个日期(行)排序,列按从低到高排序。
params = {"ytick.color" : "b",
"xtick.color" : "b",
"axes.labelcolor" : "w",
"axes.edgecolor" : "w"}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize = (14, 4))
ax = pyplot.gca();
extent = (0, matrix.shape[0], matrix.shape[1], 0)
ax.imshow(matrix.transpose(), cmap=pyplot.cm.RdYlGn)
ax.set_yticks(np.arange(len(s_names)), labels = list(matrix.columns))
ax.set_yticks(np.arange(-0.5, len(s_names),1), minor=True)
ax.set_xticks(np.arange(-0.5, len(dates_),1), minor=True)
pyplot.xticks(rotation=90)
ax.set_aspect(1)
ax.grid(color="w", linewidth=3, which='minor')
If you have any further questions or need additional translations, please let me know.
英文:
I'm trying to customize the labels / ticks on the x-axis here to look like another plot I had created:
import matplotlib as mplt
from matplotlib import dates, pyplot
from matplotlib.transforms import ScaledTranslation
import numpy as np
import pandas as pd
import datetime
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # categories
dates_ = pd.date_range('2023/01/01', '2023/01/08', freq='3H', tz='utc') # Creating a list of date time for index.
matrix = pd.DataFrame(columns=s_names, index=dates_) # initializing the df.
matrix = matrix.applymap(lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))) # replacing nan in the df with random number.
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True) # Sorting by the first date (row), columns get ordered in from lowest to highest.
params = {"ytick.color" : "b",
"xtick.color" : "b",
"axes.labelcolor" : "w",
"axes.edgecolor" : "w"}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize = (14, 4))
ax = pyplot.gca();
extent = (0, matrix.shape[0], matrix.shape[1], 0)
ax.imshow(matrix.transpose(), cmap=pyplot.cm.RdYlGn)
ax.set_yticks(np.arange(len(s_names)), labels = list(matrix.columns))
ax.set_yticks(np.arange(-0.5, len(s_names),1), minor=True)
ax.set_xticks(np.arange(-0.5, len(dates_),1), minor=True)
pyplot.xticks(rotation=90)
ax.set_aspect(1)
ax.grid(color="w", linewidth=3, which='minor')
答案1
得分: 1
以下是翻译好的代码部分:
import matplotlib as mplt
from matplotlib import dates, pyplot
from matplotlib.transforms import ScaledTranslation
import numpy as np
import pandas as pd
import datetime
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 类别
# 为索引创建日期时间列表。
dates_ = pd.date_range('2023/01/01', '2023/01/08', freq='3H', tz='utc')
matrix = pd.DataFrame(columns=s_names, index=dates_) # 初始化数据框。
# 用随机数替换数据框中的NaN值。
matrix = matrix.applymap(
lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))
)
# 按照第一个日期(行)排序,列按从最低到最高的顺序排列。
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True)
params = {"ytick.color": "b",
"xtick.color": "b",
"axes.labelcolor": "w",
"axes.edgecolor": "w"}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize=(14, 4))
extent = (0, matrix.shape[0], matrix.shape[1], 0)
ax = pyplot.gca()
ax.imshow(matrix.transpose().sum().to_frame().T, cmap=pyplot.cm.RdYlGn)
# ax.set_yticks(np.arange(len(s_names)), labels=list(matrix.columns))
# ax.set_yticks(np.arange(-0.5, len(s_names), 1), minor=True)
ax.set_yticks([])
ax.set_xticks([])
pos = []
days = []
prev = None
days_labels = (
dates_.to_series().dt.day.astype(str)
+ ' '
+ dates_.to_series().dt.month_name().str[:3]
).values
for i, dm in enumerate(days_labels):
if dm != prev:
pos.append(i)
prev = dm
days.append(prev)
pos.append(len(days_labels))
pos = np.array(pos) - 0.5
# 创建垂直线,以分隔每一天
ax.vlines(pos, 0, -1, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
hours_vlines = []
for day, pos0, pos1 in zip(days, pos[:-1], pos[1:]):
# 添加日期标签
ax.text((pos0 + pos1) / 2, -0.8, day, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontweight='bold')
# pos1和pos0表示两个连续日期的x轴位置。
# 我们希望为每一天创建3条垂直线,在6点、12点和18点。
# 因此,我们需要在以下位置添加小时标签:
# - 06:00:00 -> pos0 + (pos1 - pos0) / 24 * 6
# - 12:00:00 -> pos0 + (pos1 - pos0) / 24 * 12
# - 18:00:00 -> pos0 + (pos1 - pos0) / 24 * 18
step = (pos1 - pos0) / 24
for i, hour in enumerate(['06:00:00', '12:00:00', '18:00:00']):
hp = pos0 + step * ((i + 1) * 6)
hours_vlines.append(hp)
ax.text(hp, -0.4, hour, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontsize='x-small')
# 在每一天的每个小时处创建x轴小时刻度,用于小时
# 06:00:00、12:00:00和18:00:00
ax.vlines(hours_vlines, 0, -0.1, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
ax.set_xlim(pos[0], pos[-1])
# 显示绘图
plt.tight_layout()
plt.show()
第二部分代码的翻译与第一部分相似,因此不再重复提供翻译。
英文:
Assuming your desired plot has only one row that equals the sum of all values for each given hour, you could use something like this:
import matplotlib as mplt
from matplotlib import dates, pyplot
from matplotlib.transforms import ScaledTranslation
import numpy as np
import pandas as pd
import datetime
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # categories
# Creating a list of date time for an index.
dates_ = pd.date_range('2023/01/01', '2023/01/08', freq='3H', tz='utc')
matrix = pd.DataFrame(columns=s_names, index=dates_) # initializing the df.
# replacing nan in the df with a random number.
matrix = matrix.applymap(
lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))
)
# Sorting by the first date (row), columns get ordered in from lowest to highest
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True)
params = {"ytick.color": "b",
"xtick.color": "b",
"axes.labelcolor": "w",
"axes.edgecolor": "w"}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize=(14, 4))
extent = (0, matrix.shape[0], matrix.shape[1], 0)
ax = pyplot.gca()
ax.imshow(matrix.transpose().sum().to_frame().T, cmap=pyplot.cm.RdYlGn)
# ax.set_yticks(np.arange(len(s_names)), labels=list(matrix.columns))
# ax.set_yticks(np.arange(-0.5, len(s_names), 1), minor=True)
ax.set_yticks([])
ax.set_xticks([])
pos = []
days = []
prev = None
days_labels = (
dates_.to_series().dt.day.astype(str)
+ ' '
+ dates_.to_series().dt.month_name().str[:3]
).values
for i, dm in enumerate(days_labels):
if dm != prev:
pos.append(i)
prev = dm
days.append(prev)
pos.append(len(days_labels))
pos = np.array(pos) - 0.5
# Create the vertical lines, to separate days
ax.vlines(pos, 0, -1, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
hours_vlines = []
for day, pos0, pos1 in zip(days, pos[:-1], pos[1:]):
# Add the day labels
ax.text((pos0 + pos1) / 2, -0.8, day, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontweight='bold')
# pos1 and pos0 represent the x-axis position of two consecutive days.
# We want to create 3 vertical lines for each day, at 6, 12 and 18 hours.
# So we need to add the hours' labels at:
# - 06:00:00 -> pos0 + (pos1 - pos0) / 24 * 6
# - 12:00:00 -> pos0 + (pos1 - pos0) / 24 * 12
# - 18:00:00 -> pos0 + (pos1 - pos0) / 24 * 18
step = (pos1 - pos0) / 24
for i, hour in enumerate(['06:00:00', '12:00:00', '18:00:00']):
hp = pos0 + step * ((i + 1) * 6)
hours_vlines.append(hp)
ax.text(hp, -0.4, hour, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontsize='x-small')
# Create the x-axis hours ticks at each day, for the hours
# 06:00:00, 12:00:00 and 18:00:00
ax.vlines(hours_vlines, 0, -0.1, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
ax.set_xlim(pos[0], pos[-1])
# Show the plot
plt.tight_layout()
plt.show()
Output:
If you're not trying to aggregate the values from each hour, and instead simply create a 2-level x-axis ticks, you could use something like:
import matplotlib as mplt
from matplotlib import dates, pyplot
from matplotlib.transforms import ScaledTranslation
import numpy as np
import pandas as pd
import datetime
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # categories
# Creating a list of date time for an index.
dates_ = pd.date_range('2023/01/01', '2023/01/08', freq='3H', tz='utc')
matrix = pd.DataFrame(columns=s_names, index=dates_) # initializing the df.
# replacing nan in the df with a random number.
matrix = matrix.applymap(
lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))
)
# Sorting by the first date (row), columns get ordered in from lowest to highest
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True)
params = {"ytick.color": "b",
"xtick.color": "b",
"axes.labelcolor": "w",
"axes.edgecolor": "w"}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize=(14, 4))
extent = (0, matrix.shape[0], matrix.shape[1], 0)
ax = pyplot.gca()
ax.imshow(matrix.transpose(), cmap=pyplot.cm.RdYlGn)
ax.set_yticks(np.arange(len(s_names)), labels=list(matrix.columns))
ax.set_yticks(np.arange(-0.5, len(s_names), 1), minor=True)
ax.set_xticks([])
pos = []
days = []
prev = None
days_labels = (
dates_.to_series().dt.day.astype(str)
+ ' '
+ dates_.to_series().dt.month_name().str[:3]
).values
for i, dm in enumerate(days_labels):
if dm != prev:
pos.append(i)
prev = dm
days.append(prev)
pos.append(len(days_labels))
pos = np.array(pos) - 0.5
# Create the vertical lines, to separate days
ax.vlines(pos, 0, -0.15, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
hours_vlines = []
for day, pos0, pos1 in zip(days, pos[:-1], pos[1:]):
# Add the day labels
ax.text((pos0 + pos1) / 2, -0.12, day, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontweight='bold')
# pos1 and pos0 represent the x-axis position of two consecutive days.
# We want to create 3 vertical lines for each day, at 6, 12 and 18 hours.
# So we need to add the hours' labels at:
# - 06:00:00 -> pos0 + (pos1 - pos0) / 24 * 6
# - 12:00:00 -> pos0 + (pos1 - pos0) / 24 * 12
# - 18:00:00 -> pos0 + (pos1 - pos0) / 24 * 18
step = (pos1 - pos0) / 24
for i, hour in enumerate(['06:00:00', '12:00:00', '18:00:00']):
hp = pos0 + step * ((i + 1) * 6)
hours_vlines.append(hp)
ax.text(hp, -0.06, hour, ha='center', clip_on=False,
transform=ax.get_xaxis_transform(), color='blue', fontsize='x-small')
# Create the x-axis hours ticks at each day, for the hours
# 06:00:00, 12:00:00 and 18:00:00
ax.vlines(hours_vlines, 0, -0.01, color='blue', lw=0.8, clip_on=False,
transform=ax.get_xaxis_transform())
ax.set_xlim(pos[0], pos[-1])
# Show the plot
plt.tight_layout()
plt.show()
Output:
Edit: Change vlines
Position to the Centre of the Squares
To centralize the vlines
and each square center, try this:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import pyplot
s_names = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # categories
# Creating a list of date time for an index.
dates_ = pd.date_range("2023/01/01", "2023/01/08", freq="3H", tz="utc")
matrix = pd.DataFrame(columns=s_names, index=dates_) # initializing the df.
# replacing nan in the df with a random number.
matrix = matrix.applymap(
lambda l: l if not np.isnan(l) else np.random.choice(range(1, 10))
)
# Sorting by the first date (row), columns get ordered in from lowest to highest
matrix.sort_values(by=matrix.index[0], ascending=True, axis=1, inplace=True)
params = {
"ytick.color": "b",
"xtick.color": "b",
"axes.labelcolor": "w",
"axes.edgecolor": "w",
}
pyplot.rcParams.update(params)
fig, ax = pyplot.subplots(figsize=(14, 4))
extent = (0, matrix.shape[0], matrix.shape[1], 0)
agg_matrix = matrix.transpose().sum().to_frame().T
ax = pyplot.gca()
ax.imshow(agg_matrix, cmap=pyplot.cm.RdYlGn)
ax.set_yticks([])
ax.set_xticks([])
days_vlines = []
hours_vlines = []
for i, dt in enumerate(agg_matrix.columns):
if dt.hour in [6, 12, 18]:
ax.text(
i,
-0.4,
dt.strftime("%H:%M:%S"),
ha="center",
clip_on=False,
transform=ax.get_xaxis_transform(),
color="blue",
fontsize="x-small",
)
hours_vlines.append(i)
if dt.hour == 12:
ax.text(
i,
-0.9,
f"{dt.day} {dt.month_name()[:3]}",
ha="center",
clip_on=False,
transform=ax.get_xaxis_transform(),
color="blue",
fontweight="bold",
)
if dt.hour == 0:
days_vlines.append(i)
# Create the x-axis hours ticks at each day, for the hours
# 06:00:00, 12:00:00 and 18:00:00
ax.vlines(
hours_vlines,
0,
-0.1,
color="blue",
lw=0.8,
clip_on=False,
transform=ax.get_xaxis_transform(),
)
ax.vlines(
days_vlines, 0, -1, color="blue", lw=0.8, clip_on=False, transform=ax.get_xaxis_transform()
)
# Show the plot
plt.tight_layout()
plt.show()
Output:
For the first day:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论