#python #matplotlib
#питон #matplotlib
Вопрос:
У меня есть некоторые данные, которые я хотел бы отобразить, состоящие из двух столбцов, один из которых представляет собой сумму count
, а другой столбец — фактическую дату записи. При построении этого графика, поскольку у меня более 2000 дат, имеет смысл не показывать каждую отдельную дату в виде галочки на x
оси -, иначе она не будет читаемой. Тем не менее, мне трудно заставить даты отображаться на x
оси — с помощью какой-то логики. Я пробовал использовать встроенные локаторы тиков для matplotlib, но это почему-то не работает. Вот предварительный просмотр данных:
PatientTraffic = pd.DataFrame({'count' : CleanData.groupby("TimeStamp").size()}).reset_index()
display(PatientTraffic.head(3000))
TimeStamp count
0 2016-03-13 12:20:00 1
1 2016-03-13 13:39:00 1
2 2016-03-13 13:43:00 1
3 2016-03-13 16:00:00 1
4 2016-03-14 13:27:00 1
... ... ...
2088 2020-02-18 16:00:00 8
2089 2020-02-19 16:00:00 8
2090 2020-02-20 16:00:00 8
2091 2020-02-21 16:00:00 8
2092 2020-02-22 16:00:00 8
2093 rows × 2 columns
и когда я перейду к построению графика с этими настройками:
PatientTrafficPerTimeStamp = PatientTraffic.plot.bar(
x='TimeStamp',
y='count',
figsize=(20,3),
title = "Patient Traffic over Time"
)
PatientTrafficPerTimeStamp.xaxis.set_major_locator(plt.MaxNLocator(3))
Я ожидаю получить гистограмму, где x
ось — имеет три отметки, по одной в начале, середине и конце … возможно, я использую это неправильно. Кроме того, кажется, что появляющиеся галочки — это просто первые 3 в столбце, а это не то, что я хочу. Любая помощь будет признательна!
Ответ №1:
Вы, вероятно, думаете, что у вас одна проблема, но на самом деле у вас их две — и обе основаны на том факте, что вы используете удобные функции. Проблема, о которой вы, скорее всего, не знаете, заключается в том, что pandas отображает столбцы как категориальные данные. Это имеет смысл в большинстве условий, но, очевидно, нет, если у вас есть данные временной метки в качестве оси x. Давайте посмотрим, не выдумал ли я это:
import matplotlib.pyplot as plt
import pandas as pd
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
df = pd.read_csv("test.txt", sep = "s{2,}", engine="python")
#convert TS from string into datetime objects
df.TS = pd.to_datetime(df.TS, format="%Y-%m-%d %H:%M:%S")
#and plot it as you do directly from pandas that provides the data to matplotlib
df.plot.bar(
x="TS",
y="Val",
ax=ax1,
title="pandas version"
)
#now plot the same data using matplotlib
ax2.bar(df.TS, df.Val, width=22)
ax2.tick_params(axis="x", labelrotation=90)
ax2.set_title("matplotlib version")
plt.tight_layout()
plt.show()
Пример вывода:
Итак, мы должны отображать их непосредственно из matplotlib, чтобы предотвратить потерю информации о временных метках. Очевидно, что мы теряем некоторый комфорт, предоставляемый pandas, например, нам приходится регулировать ширину столбцов и помечать оси. Теперь вы могли бы использовать другую удобную функцию MaxNLocator
, но, как вы заметили, она была написана так, чтобы хорошо работать для большинства условий, но вы отказываетесь от контроля над точным расположением тиков. Почему бы не написать наш собственный локатор, используя FixedLocator
?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import FixedLocator
import pandas as pd
def myownMaxNLocator(datacol, n):
datemin = mdates.date2num(datacol.min())
datemax = mdates.date2num(datacol.max())
xticks = np.linspace(datemin, datemax, n)
return xticks
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
df = pd.read_csv("test.txt", sep = "s{2,}", engine="python")
df.TS = pd.to_datetime(df.TS, format="%Y-%m-%d %H:%M:%S")
df.plot.bar(
x="TS",
y="Val",
ax=ax1,
title="pandas version"
)
ax2.bar(df.TS, df.Val, width=22)
ax2.set_title("matplotlib version")
dateticks = myownMaxNLocator(df.TS, 5)
ax2.xaxis.set_major_locator(FixedLocator(dateticks))
ax2.tick_params(axis="x", labelrotation=90)
plt.tight_layout()
plt.show()
Здесь отметки начинаются с наименьшего значения и заканчиваются наибольшим значением. В качестве альтернативы вы можете использовать LinearLocator
функцию, которая равномерно распределяет пометки по всему представлению:
from matplotlib.ticker import LinearLocator
...
ax2.bar(df.TS, df.Val, width=22)
ax2.set_title("matplotlib version")
ax2.xaxis.set_major_locator(LinearLocator(numticks=5))
ax2.tick_params(axis="x", labelrotation=90)
...
Образцы данных были сохранены в файле со следующей структурой:
TS Val
0 2016-03-13 12:20:00 1
1 2016-04-13 13:39:00 3
2 2016-04-03 13:43:00 5
3 2016-06-17 16:00:00 1
4 2016-09-14 13:27:00 2
2088 2017-02-08 16:00:00 7
2089 2017-02-25 16:00:00 2
2090 2018-02-20 16:00:00 8
2091 2019-02-21 16:00:00 9
2092 2020-02-22 16:00:00 8
Комментарии:
1. Привет! Да, это было очень полезно, спасибо. В итоге я пока использовал только плоттер Matplotlib, потому что это просто немного упростило задачу. Очень, очень ценю это, спасибо!
Ответ №2:
Рассматривали ли вы возможность группировки по дате, если вам не нужно так много xticks? Отвечая на ваш вопрос, вы можете сделать пользовательские пометки с помощью :
plt.xticks(тики=[любой список], метки=[список меток])
ссылка на документацию