У меня есть данные за все время, что я потратил на программирование. Эти данные представлены в виде словаря, где ключом является дата, а значением — список кортежей, содержащих время начала сеанса кодирования и продолжительность сеанса кодирования.
Я успешно изобразил это на сломанной_полосе, используя приведенный ниже код, где ось Y — это дата, ось X — время в этот день, а каждая сломанная полоса — это отдельный сеанс.
for i,subSessions in enumerate(sessions.values()):
plt.broken_barh(subSessions, (i,1))
months = {}
start = getStartMonth()
for month in period_range(start=start,end=datetime.today(),freq = "M"):
month = str(month)
months[month] = (datetime.strptime(month,'%Y-%m')-start).days
plt.yticks(list(months.values()),months.keys())
plt.xticks(range(0,24*3600,3600),[str(i)+":00" for i in range(24)],rotation=45)
plt.gca().invert_yaxis()
plt.show()
Я хочу использовать эти данные, чтобы узнать, в какое время дня я трачу больше всего времени на программирование, но из приведенной выше диаграммы это не очень ясно, поэтому я хотел бы отобразить их в виде линейного графика или тепловой карты, где ось Y — это количество дней, которые я потратил на программирование в то время по оси x (или, другими словами, сколько сеансов присутствует в этом столбце на приведенной выше диаграмме). Как мне это сделать?
Вы можете найти отличные примеры создания тепловой карты на сайте matplotlib.
Вот базовый код с некоторыми случайными данными:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
index_labels = np.arange(0,24)
column_labels = pd.date_range(start='1/1/2022', end='1/31/2022').strftime('%m/%d')
#random data
np.random.seed(12345)
data = np.random.randint(0,60, size=(len(index_labels), len(column_labels)))
df = pd.DataFrame(data=data, columns=column_labels, index=index_labels)
#heatmap function
def heatmap(df, ax, cbarlabel = "", cmap = "Greens", label_num_dec_place=0):
df = df.copy()
# Ploting a blank heatmap
im = ax.imshow(df.values, cmap)
# create a customized colorbar
cbar = ax.figure.colorbar(im, ax=ax, fraction=0.05, extend='both', extendfrac=0.05)
cbar.ax.set_ylabel(cbarlabel, rotation=-90, va = "bottom", fontsize=14)
# Setting ticks
ax.set_xticks(np.arange(df.shape[1]), labels=df.columns, fontsize=12)
ax.set_yticks(np.arange(df.shape[0]), labels=list(df.index), fontsize=12)
# proper placement of ticks
ax.tick_params(axis='x', top=True, bottom=False,
labeltop=True, labelbottom=False)
ax.spines[:].set_visible(False)
ax.grid(which = "both", visible = "False", color = "white", linestyle='solid', linewidth=2)
ax.grid(False)
# Rotation of tick labels
plt.setp(ax.get_xticklabels(), rotation=-60,
ha = "right", rotation_mode=None)
plt.setp(ax.get_yticklabels(), rotation=30)
#plotting and saving
fig, ax = plt.subplots(facecolor=(1,1,1), figsize=(20,8), dpi=200)
heatmap(df=df, ax=ax, cbarlabel = "time (min)", cmap = "Greens", label_num_dec_place=0)
plt.savefig('time_heatmap.png',
bbox_inches='tight',
facecolor=fig.get_facecolor(),
transparent=True,
)
Вывод:
Один из способов сделать это — использовать выборку. Выберите, сколько выборок вы хотите взять в заданном интервале (точность, например, 288 выборок в день), разделите каждый интервал на это количество выборок и подсчитайте, сколько сеансов в этой выборке. Недостатком этого является то, что он не может быть точным на 100%, а увеличение точности увеличивает время, необходимое для создания (у меня требуется несколько минут для создания изображения с точностью до секунды, хотя этот уровень точности практически не делает ничего). разница в результате).
Вот некоторый код, который может создавать как тепловую карту, так и линейный график.
# Configuration options
precisionPerDay = 288
timeTicksPerDay = 24
timeTickRotation = 60
timeTickFontSize = 6
heatmap = True
# Constants
hoursInDay = 24
secondsInHour = 3600
secondsInDay = hoursInDay*secondsInHour
xInterval = secondsInDay/precisionPerDay
timeTickSecondInterval = precisionPerDay/timeTicksPerDay
timeTickHourInterval = hoursInDay/timeTicksPerDay
# Calculating x-axis (time) ticks
xAxis = range(precisionPerDay)
timeTickLabels = []
timeTickLocations = []
for timeTick in range(timeTicksPerDay):
timeTickLocations.append(int(timeTick*timeTickSecondInterval))
hours = timeTick/timeTicksPerDay*hoursInDay
hour = int(hours)
minute = int((hours-hour)*60)
timeTickLabels.append(f"{hour:02d}:{minute:02d}")
# Calculating y-axis (height)
heights = []
for dayX in xAxis:
rangeStart = dayX*xInterval
rangeEnd = rangeStart+xInterval
y = 0
for date,sessions in sessions.items():
for session in sessions:
if session[0] < rangeEnd and session[0]+session[1] > rangeStart:
y += 1
heights.append(y)
# Plotting data
if heatmap:
plt.yticks([])
plt.imshow([heights], aspect = "auto")
else:
plt.plot(xAxis,heights)
plt.ylim(ymin=0)
plt.xlim(xmin=0,xmax=len(heights))
plt.xlabel("Time of day")
plt.ylabel("How often I've coded at that time")
plt.xticks(timeTickLocations,timeTickLabels,
fontsize=timeTickFontSize,rotation=timeTickRotation)
plt.show()
А вот и примеры результатов
График, созданный с использованием тех же параметров конфигурации, что и в приведенном выше коде.
Те же данные, но в виде линейного графика с меньшей точностью (24 в день) и большим количеством тактов (48)
Это доказывает как пример того, что я ищу, но я бы предпочел метод, который является точным на 100% и генерирует быстрее. Если такой метод будет предоставлен в качестве ответа, я приму это, иначе это будет принятый ответ
Извините, проблема не в построении данных, а в их получении. Кроме того, для моей проблемы потребуется одномерная тепловая карта, поскольку ось Y будет отображаться через цвет.