I’m extremely new to matplotlib and I’m trying to create a discord bot in python that simply logs the server activity and displays it as a line graph based on what time the server is most active (msgs per 10sec / time of day). The problem is, the number of messages is different from the amount of seconds in a day 99.99% of the time, so the 2 lists don’t match and I get
JavaScript
x
2
1
ValueError: x and y must have same first dimension,
2
My lists are made like this:
JavaScript
1
3
1
instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]
2
times= [0, 3600, 7200, 10800, 14400, 18000, 21600, 25200, 28800, 32400, 36000, 39600, 43200, 46800, 50400, 54000, 57600, 61200, 64800, 68400, 72000, 75600, 79200]
3
So I have a list of (what time, how many msgs) and a list of each hour of the day in seconds, So I could graph them as this many msgs happened at this time of day.
How would I plot them onto a single graph, because ax.plot (instances, times)
gives me the error. I have searched other answers, but none have helped me.
Advertisement
Answer
- In this case, the data is displayed better as a barplot.
- Extract seconds and values from
instances
to separate variable - Convert the secondes to a standard time format of
H:M:S
- It doesn’t seem like
times
is required.
JavaScript
1
29
29
1
import matplotlib.pyplot as plt
2
import seaborn as sns
3
from datetime import timedelta
4
5
instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]
6
7
# separate out x and convert from seconds to H:M:S
8
x = [timedelta(seconds=v[0]) for v in instances]
9
10
# separate out the y values
11
y = [v[1] for v in instances]
12
13
# sort x and y, based on time
14
x, y = zip(*sorted(zip(x, y)))
15
16
# convert from tuples to lists because barplot won't work with tuples
17
x, y = list(x), list(y)
18
19
# plot
20
fig = plt.figure(figsize=(10, 5))
21
p = sns.barplot(x=x, y=y)
22
p.set(xlabel='Observation Time', ylabel='Activity Count', title='Messages')
23
24
# annotate
25
p.bar_label(p.containers[0], label_type='edge', padding=1)
26
27
# pad the spacing between the number and the edge of the figure
28
p.margins(y=0.1)
29
- Plot as a line plot
JavaScript
1
24
24
1
import matplotlib.pyplot as plt
2
import matplotlib.ticker as mticker
3
from datetime import timedelta
4
5
instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]
6
7
# separate out the x values
8
x = [v[0] for v in instances]
9
10
# separate out the y values
11
y = [v[1] for v in instances]
12
13
# sort x and y, based on time
14
x, y = zip(*sorted(zip(x, y)))
15
16
fig, ax = plt.subplots(figsize=(10, 5))
17
18
ax.plot(x, y, marker='o')
19
20
# fixing xticks with matplotlib.ticker "FixedLocator"
21
xticks_loc = ax.get_xticks()
22
ax.xaxis.set_major_locator(mticker.FixedLocator(xticks_loc))
23
_ = ax.set_xticklabels([timedelta(seconds=tm) for tm in xticks_loc])
24
- If you want more hours on the xaxis, instead of using
ax.get_xticks
, pass alist
of values, or userange
)xticks_loc = range(0, 86400, 3600)
and also addax.set_xlim(0, 86400)
- Probably increase the width of the figure
fig, ax = plt.subplots(figsize=(15, 5))
- Rotate the xaxis labels
_ = ax.set_xticklabels([timedelta(seconds=tm) for tm in xticks_loc], rotation=45)
JavaScript
1
8
1
# fixing xticks with matplotlib.ticker "FixedLocator"
2
ax.set_xlim(0, 86400)
3
4
xticks_loc = range(0, 86400, 3600)
5
6
ax.xaxis.set_major_locator(mticker.FixedLocator(xticks_loc))
7
_ = ax.set_xticklabels([timedelta(seconds=tm) for tm in xticks_loc], rotation=45)
8