Skip to content
Advertisement

Problem with plotting two lists with different sizes using matplotlib

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

ValueError: x and y must have same first dimension, 

My lists are made like this:

instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]
times= [0, 3600, 7200, 10800, 14400, 18000, 21600, 25200, 28800, 32400, 36000, 39600, 43200, 46800, 50400, 54000, 57600, 61200, 64800, 68400, 72000, 75600, 79200]

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.
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import timedelta

instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]

# separate out x and convert from seconds to H:M:S
x = [timedelta(seconds=v[0]) for v in instances]

# separate out the y values
y = [v[1] for v in instances]

# sort x and y, based on time
x, y = zip(*sorted(zip(x, y)))

# convert from tuples to lists because barplot won't work with tuples
x, y = list(x), list(y)

# plot
fig = plt.figure(figsize=(10, 5))
p = sns.barplot(x=x, y=y)
p.set(xlabel='Observation Time', ylabel='Activity Count', title='Messages')

# annotate
p.bar_label(p.containers[0], label_type='edge', padding=1)

# pad the spacing between the number and the edge of the figure
p.margins(y=0.1)

enter image description here

  • Plot as a line plot
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from datetime import timedelta

instances= [(6209, 10), (42906, 20), (70182, 23), (30422, 18), (70503, 21), (8426, 12), (78322, 22), (27038, 17), (1453, 2), (36138, 3)]

# separate out the x values
x = [v[0] for v in instances]

# separate out the y values
y = [v[1] for v in instances]

# sort x and y, based on time
x, y = zip(*sorted(zip(x, y)))

fig, ax = plt.subplots(figsize=(10, 5))

ax.plot(x, y, marker='o')

# fixing xticks with matplotlib.ticker "FixedLocator"
xticks_loc = ax.get_xticks()
ax.xaxis.set_major_locator(mticker.FixedLocator(xticks_loc))
_ = ax.set_xticklabels([timedelta(seconds=tm) for tm in xticks_loc])

enter image description here

  • If you want more hours on the xaxis, instead of using ax.get_xticks, pass a list of values, or use range) xticks_loc = range(0, 86400, 3600) and also add ax.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)
# fixing xticks with matplotlib.ticker "FixedLocator"
ax.set_xlim(0, 86400)

xticks_loc = range(0, 86400, 3600)

ax.xaxis.set_major_locator(mticker.FixedLocator(xticks_loc))
_ = ax.set_xticklabels([timedelta(seconds=tm) for tm in xticks_loc], rotation=45)

enter image description here

Advertisement