Timestamp overlapping matplotlib - python

I am trying to create a graph using matplotlib with number of requests (y-axis) vs timestamp (x-axis in HH:MM format).
This graph will show the pattern for the all the requests received between 6:00 AM to 6:00 PM. Below is the sample data. Actual data has more than 500 entries.
time_stamp = ['06:02', '06:03', '06:12', '06:16', '06:17', '06:27', '06:28', '06:30', '06:31', '06:34', '06:35', '06:36', '06:37', '06:38', '06:39', '06:40', '06:41', '06:42', '06:43']
requests = [74, 20, 2, 1, 11, 9, 34, 3, 5, 4, 28, 77, 75, 73, 122, 99, 170, 79, 44, 79, 100, 58, 104, 84, 77, 98, 27]
Below is the script which I am using to generate the graph. Problem which I am facing currently is overlapping of all the timestamps on the x-axis.
Script:
import matplotlib.pyplot as plt
TITLE = 'Time (Per Minute) Vs Num of Requests Graph'
X_AXIS_NAME = 'TimeStamps (per minute)'
Y_AXIS_NAME = 'No. of Requests'
time_stamp = ['06:02', '06:03', '06:12', '06:16', '06:17', '06:27', '06:28',
'06:30', '06:31', '06:34', '06:35', '06:36', '06:37', '06:38', '06:39',
'06:40', '06:41', '06:42', '06:43', '06:44', '06:45', '06:46', '06:47',
'06:48', '06:49', '06:50', '06:51', '06:52', '06:53', '06:54', '06:55',
'06:56', '06:57', '06:58', '06:59', '07:00', '07:01']
requests = [74, 20, 2, 1, 11, 9, 34, 3, 5, 4, 28, 77, 75, 73]
fig, ax = plt.subplots()
plt.plot(time_stamp, requests)
fig.autofmt_xdate()
plt.xlabel(X_AXIS_NAME)
plt.ylabel(Y_AXIS_NAME)
plt.title(TITLE)
plt.show()
fig.savefig('graph.png', dpi=fig.dpi)
Generated Graph:
And this is the graph which I actually want to generate. This graph has been generated using Excel.
Expected Graph:
Timestamps are not overlapped.
EDIT 1:
dates = []
for ts in time_stamp:
dates.append( datetime.strptime(ts, '%H:%M'))
mp_dates = matplotlib.dates.date2num(dates)
matplotlib.pyplot.plot_date(mp_dates, requests)
EDIT 2:
dates = []
for ts in time_stamp:
local_d = datetime.strptime(ts, '%H:%M')
dates.append( local_d)
fig, ax = plt.subplots()
plt.setp( ax.xaxis.get_majorticklabels(), rotation=90)
plt.plot(dates, requests)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
#fig.autofmt_xdate()
plt.xlabel(X_AXIS_NAME)
plt.ylabel(Y_AXIS_NAME)
plt.title(TITLE)
# function to show the plot
plt.show()
fig.savefig('graph.png', dpi=fig.dpi)
Only missing piece is to reduce the interval between 2 ticks. Currently it is 2 hours.
Any help or pointer in this regards is highly appreciated.

For just fully rotating the labels like in your excel plot. Try this.
plt.setp( ax.xaxis.get_majorticklabels(), rotation=90)

After doing more research finally I am able to plot it.
dates = []
for ts in time_stamp:
local_d = datetime.strptime(ts, '%H:%M')
dates.append( local_d)
fig, ax = plt.subplots()
plt.setp( ax.xaxis.get_majorticklabels(), rotation=90)
plt.plot(dates, requests)
ax.xaxis.set_major_locator(mdates.MinuteLocator(interval=20))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
plt.xlabel(X_AXIS_NAME)
plt.ylabel(Y_AXIS_NAME)
plt.title(TITLE)
plt.show()
fig.savefig('graph.png', dpi=fig.dpi)
Thanks to the community!

The problem is not the many data but the density of tick labels. autofmt_xdate even fails with a few labelled ticks if the figure is narrow. So the solution is to reduce the number of labelled ticks. No rotation is needed if only full hours are labelled without printing minutes. Note that MinuteLocator(interval=60) would fail -- silently placing ticks with an offset of a fractional hour.
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from numpy import arange # for fake x data
y = [3, 30, 3000, 2900, 3100, 1000, 3000, 2000, 200, 20, 2] # roughly
x = arange(len(y))*dt.timedelta(seconds=4800) + dt.datetime.strptime('05:50', '%H:%M')
fig, ax = plt.subplots(figsize=(10,4))
ax.set_title('Request Load (<server> <service> <date>)')
ax.set_xlabel('time of day in hours (timezone)')
ax.set_ylabel('requests per minute')
ax.plot(x, y)
ax.xaxis.set_minor_locator(mdates.MinuteLocator(interval=15))
ax.xaxis.set_major_locator(mdates.HourLocator(interval=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H'))
ax.set_ylim(0)
fig.tight_layout()
fig.show()

You can actually use matplotlib's autofmt_xdate() method to solve the problem you're facing.
Just add following line before plt.show()
plt.gcf().autofmt_xdate()
The defaults work well, so most probably you can just call it without any parameters, but for the sake of completeness, you can use parameters specified below.
Quoting matplotlib documentation (v.3.1.1):
autofmt_xdate(self, bottom=0.2, rotation=30, ha='right', which=None)
Date ticklabels often overlap, so it is useful to rotate them and right align them. Also, a common use case is a number of subplots with shared xaxes where the x-axis is date data. The ticklabels are often long, and it helps to rotate them on the bottom subplot and turn them off on other subplots, as well as turn off xlabels.
Parameters:
bottom : scalar
The bottom of the subplots for subplots_adjust().
rotation : angle in degrees
The rotation of the xtick labels.
ha : string
The horizontal alignment of the xticklabels.
which : {None, 'major', 'minor', 'both'}
Selects which ticklabels to rotate. Default is None which works the same as major

Related

How to update y-axis in matplotlib

I have problem update limits on y-axis.
My idea is to read some csv file, and to plot some graphs.
When I set limits for y-axis, it doesn't show on the plot.
It always shows, values from file.
I'm new in python.
import matplotlib.pyplot as plt
import csv
import numpy as np
x = []
y = []
chamber_temperature = []
with open(r"C:\Users\mm02058\Documents\test.txt", 'r') as file:
reader = csv.reader(file, delimiter = '\t')
for row in (reader):
x.append(row[0])
chamber_temperature.append(row[1])
y.append(row[10])
x.pop(0)
y.pop(0)
chamber_temperature.pop(0)
#print(chamber_temperature)
arr = np.array(chamber_temperature)
n_lines = len(arr)
time = np.arange(0,n_lines,1)
time_sec = time * 30
time_min = time_sec / 60
time_hour = time_min / 60
time_day = time_hour / 24
Fig_1 = plt.figure(figsize=(10,8), dpi=100)
plt.suptitle("Powered Thermal Cycle", fontsize=14, x=0.56, y= 0.91)
plt.subplot(311, xlim=(0, 30), ylim=(-45,90), xticks=(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30), yticks=( -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90), ylabel=("Temperature [°C]"))
plt.plot(time_hour, chamber_temperature, 'k', label='Temperature')
plt.gca().invert_yaxis()
plt.grid()
plt.legend(shadow=True, fontsize=('small'), loc = 'center right', bbox_to_anchor=(1.13, 0.5))
plt.show()
Your code looks suspicious, because I cannot see a conversion from strings (what csv.reader produces) to floating point numbers.
Also your plot look suspicious, because the y tick labels are not sorted!
I decided to check if, by chance, Matplotlib tries to be smarter than it should...
import numpy as np
import matplotlib.pyplot as plt
# let's plot an array of strings, as I suppose you did,
# and see if Matplotlib doesn't like it, or ...
np.random.seed(20210719)
arr_of_floats = 80+10*np.random.rand(10)
arr_of_strings = np.array(["x = %6.3f"%round(x, 2) for x in arr_of_floats])
plt.plot(range(10), arr_of_strings)
plt.show()
Now, let's see what happens if we perform the conversion to floats
# for you it's simply: array(chamber_temperature, dtype=float)
arr_of_floats = np.array([s[4:] for s in arr_of_strings], dtype=float)
plt.plot(range(10), arr_of_floats)
plt.show()
Eventually, do not change axes' limits (etc etc) BEFORE plotting, but:
first, possibly organize your figure (figure size, subplots, etc)
second, plot your data,
third, adjust the details of the graph and
fourth and last, commit your work using plt.show().
Use
plt.ylim([bottom limit, top limit]) #like plt.ylim(84,86)
before your
plt.show()
that should work!
You are setting your x and y lims, as you have the equal sign.
You need to call them like a function (no equal sign).

Plotting Time Series with Matplotlib: Using datetime.datetime() works but datetime.datetime.strptime(str, format) does not

I wish to plot a some data in a bar graph using matplotlib. The x-values of the plot should be datetime.datetime objects so that matplotlib can use them. If I generate the time values with the following method it works:
import matplotlib.pyplot as plt
import datetime
x = [datetime.datetime(2010, 12, 1, 10, 0),
datetime.datetime(2011, 1, 4, 9, 0),
datetime.datetime(2011, 5, 5, 9, 0)]
y = [4, 9, 2]
ax = plt.subplot(111)
ax.bar(x, y, width=10)
ax.xaxis_date()
plt.show()
Yielding this plot:
However if I use the following method it does not work:
import matplotlib.pyplot as plt
import datetime
dates = ["2020-05-11 18:25:37","2020-05-11 18:25:40","2020-05-11 18:25:43",
"2020-05-11 18:25:46","2020-05-11 18:25:49"]
X = []
for date in dates:
X.append(datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S'))
Y = [1, 3, 4, 6, 4]
ax = plt.subplot(111)
ax.bar(X, Y, width=10)
ax.xaxis_date()
plt.show()
Yielding this abomination:
I am obviously missing something here but it appears to me that the results should be the same for:
datetime.datetime(2010, 12 ,1 ,10, 0)
datetime.datetime.strptime('2010-12-01 10:00:00','%Y-%m-%d %H:%M:%S')
You can use date2num to convert the dates to matplotlib format.
Plot the dates and values using plot_date:
X = matplotlib.dates.date2num(X)
matplotlib.pyplot.plot_date(X,Y)
matplotlib.pyplot.show()
or you can use like this
ax.plot_date(X, Y)
The bar graph cannot be displayed due to an error, while the line graph shows the x-axis in minutes:seconds. I'm sorry I can't help you.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
fig = plt.figure(figsize=(8,4.5))
ax = fig.add_subplot(111)
dates = ["2020-05-11 18:25:37","2020-05-11 18:25:40","2020-05-11 18:25:43",
"2020-05-11 18:25:46","2020-05-11 18:25:49"]
Y = [1, 3, 4, 6, 4]
df = pd.DataFrame({'Y':Y}, index=pd.to_datetime(dates))
ax.plot(df.index, df['Y'])
xloc = mdates.SecondLocator(interval=1)
xfmt = mdates.DateFormatter("%M:%S")
ax.xaxis.set_major_locator(xloc)
ax.xaxis.set_major_formatter(xfmt)
plt.show()
The problem was caused by a datetime width for matplotlib having to be expressed in units of 1 day. So if width = 1, a bar width is equal to a day on the x-axis.
This was resolved by making the width be equal to a percentage of a day appropriate for the time scale used, in this case 3 seconds. For example, if you want the bar width to occupy 3 seconds on the x-axis make the width equal to 3 seconds as a percentage of a whole day,
#NB: There are 86400 seconds in a day and I want a width of 3 seconds.
ax.plot(width = (1/(86400))*3)
If you wish for the bars to not touch you should reduce the width of the bars to less than the interval between timestamps as plotted on the x-axis. Further if you wish to dyamically determine the minimum interval between timestamps please look at this post.

matplotlib - autofmt_xdate() fails to rotate x axis labels after axes.cla() is called

I have two sets of data thats needs to be plotted against time.
And I need to display them individually and together with the help of a radio button (or a similar way). The radio button code is based on https://stackoverflow.com/a/6697555/3910296
Everything looks good till the first set of data is loaded.
I clear the axes and re-plot the data whenever the next set of data is to be plotted. But when I click the next item in radio button, the new data is displayed, but x axis is not rotated. Is there any way to fix this.
Sample code to reproduce the problem I am facing
import datetime
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
import matplotlib.dates as mdates
data0_usage = [45, 76, 20, 86, 79, 95, 14, 94, 59, 84]
data1_usage = [57, 79, 25, 28, 17, 46, 29, 52, 68, 92]
data0_timestamp = []
def draw_data_plot(ax, data_no):
if data_no == 0:
data_usage = data0_usage
data_color = 'go'
elif data_no == 1:
data_usage = data1_usage
data_color = 'ro'
ax.plot_date(data0_timestamp, data_usage, data_color)
ax.plot_date(data0_timestamp, data_usage, 'k', markersize=1)
def draw_plot():
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(True)
# Adjust the subplots region to leave some space for the sliders and buttons
fig.subplots_adjust(left=0.25, bottom=0.25)
# Beautify the dates on x axis
time_format = mdates.DateFormatter('%Y-%b-%d %H:%M:%S')
plt.gca().xaxis.set_major_formatter(time_format)
plt.gcf().autofmt_xdate()
# Draw data0 plot
draw_data_plot(ax, 0)
# Add a set of radio buttons for changing color
color_radios_ax = fig.add_axes(
[0.025, 0.5, 0.15, 0.15], axisbg='lightgoldenrodyellow')
color_radios = RadioButtons(
color_radios_ax, ('data 0', 'data 1', 'all'),
active=0)
def color_radios_on_clicked(label):
ax.cla()
ax.grid(True)
if label == 'all':
draw_data_plot(ax, 0)
draw_data_plot(ax, 1)
else:
draw_data_plot(ax, int(label[5]))
ax.xaxis.set_major_formatter(time_format)
plt.gcf().autofmt_xdate()
fig.canvas.draw_idle()
color_radios.on_clicked(color_radios_on_clicked)
plt.show()
current_date = datetime.datetime.today()
for days in range(0, 10):
data0_timestamp.append(current_date + datetime.timedelta(days))
draw_plot()
Using Windows 10, Python 2.7.32, matplotlib 2.1.0
The problem
The reason plt.gcf().autofmt_xdate() fails for subsequent updates of the axes content is that at the point when it is called, there are axes inside the figure, which are no subplots. This is the axes created by fig.add_axes, being the radiobutton axes. autofmt_xdate will not know what to do with this axes, i.e. it would not know if this is the axes for which to rotate the labels or not, hence it will decide not to do anything. In the matplotlib source code this looks like
allsubplots = all(hasattr(ax, 'is_last_row') for ax in self.axes)
if len(self.axes) == 1:
# rotate labels
else:
if allsubplots:
for ax in self.get_axes():
if ax.is_last_row():
#rotate labels
else:
#set labels invisible
Because you have an axes, which is not a subplot, allsubplots == False and no rotation takes place.
The solution
The solution would be not to use autofmt_xdate(), but to do the work of rotating the ticklabels manually - which is really only 3 lines of code. Replace the line plt.gcf().autofmt_xdate() by
for label in ax.get_xticklabels():
label.set_ha("right")
label.set_rotation(30)

Python mathplotlib - overlapping texts in xticks and adding percentage symbol in yticks

The following is my Python code for generating a bar chart:
import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt
objects = ('Increasing operational efficiency',
'Informing strategic direction',
'Better customer service',
'Identifying and developing new products',
'Enhanced customer experience',
'Identifying new markets',
'Faster go to market',
'Complying with regulations',
'Other')
y_pos = np.arange(len(objects))
performance = [51, 36, 27, 24, 20, 11, 8, 6, 3]
plt.bar(y_pos, performance, align='center', alpha=0.5)
plt.xticks(y_pos, objects)
plt.show()
In the output the xticks are getting overlapped, is there any way to overcome it. My second doubt is in the yticks the values are coming from 0 to 60 with a interval of 10, is there any way to add a '%' symbol along with the number like 0%, 10%, ..., 60% rather than 0, 10, ..., 60.
Thank you for the help, I am new to mathplotlib
you would have found answers to your questions with a simple search...
you can rotate the x-axis labels using plt.gcf().autofmt_xdate()
for the percent signs on the y-axis, use
ax = plt.gca()
vals = ax.get_yticks()
ax.set_yticklabels(['{:.0f}%'.format(x) for x in vals])

Custom X-Axis Date Range Using Matplotlib

This is my first time asking a Python question online. I have always been able to find answers to my questions on this site..until now. I am trying to plot data that were developed using the Index Sequential Method, which is a technique for projecting historical data into the future. I have 105 charts that each cover 47 years of data. The first chart x-axis ranges from 1906-1952, the second 1907-1953, thir 1908-1954, etc. My problem is when I get to 1963, which is when the 47th year reverts back to the begining (1906). So the 1963 chart xaxis would look like this: 1963, 1964, 1965,...2008,2009,2010,1906. The 1964 chart xaxis would look like this: 1964, 1965, 1967,...2009, 2010, 1906, 1907.
I can get the data to plot fine, I just need help figuring out how to format the xaxis to accept the unique wrap-around situation when it occurs.
There are three charts per page (ax1, ax2, and ax3). yearList and chartList are the x and y data, respectively. The code below is part of a for loop that creates the yearList and chartList data sets, and it creates the charts with the wrong xaxis labels.
import matplotlib, pyPdf
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as tkr
from matplotlib.ticker import MultipleLocator
import matplotlib.figure as figure
plt.rcParams['font.family'] = 'Times New Roman'
locator = mdates.YearLocator(2)
minorLocator = MultipleLocator(1)
dateFmt = mdates.DateFormatter('%Y')
datemin = min(yearList)
datemax = max(yearList)
fig, (ax1, ax2, ax3) = plt.subplots(3,1,sharex=False)
#3X3 Top to bottom
ax1.bar(yearList1, chartList1, width=200, align='center')
ax2.bar(yearList2, chartList2, width=200, align='center')
ax3.bar(yearList3, chartList3, width=200, align='center')
axList = [ax1, ax2, ax3]
for ax in axList:
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(dateFmt)
ax.xaxis.set_minor_locator(minorLocator)
ax.set_xlim(datemin - timedelta(365), datemax + timedelta(365))
ax.grid(1)
ax.set_ylim(0,30)
ax.set_yticks(np.arange(0, 31, 5))
ax.yaxis.set_minor_locator(minorLocator)
#Rotate tick labels 90 degrees
xlabels = ax.get_xticklabels()
for label in xlabels:
label.set_rotation(90)
fig.tight_layout()
plt.subplots_adjust(right=0.925)
plt.savefig('%s\\run.pdf' % outDir)
You are making a bar graph, which means the x-posistion has little to no meaning aside from the labels, so don't try to plot the bars vs their date, plot them against the integers, and then label them as you wish:
from itertools import izip
fig, axeses = plt.subplots(3,1,sharex=False)
#3X3 Top to bottom
for yl, cl, ax in izip([yearList1, yearList2, yearList3],
[chartList1, chartList2, chartist3],
axeses):
ax.bar(range(len(cl)), cl, align='center')
ax.set_ylim(0,30)
ax.set_yticks(np.arange(0, 31, 5))
ax.yaxis.set_minor_locator(minorLocator)
xlabels = [dateFmt(xl) for xl in yl] # make a list of formatted labels
ax.set_xticks(range(len(cl))) # put the tick markers under your bars
ax.set_xticklabels(xlabels) # set the labels to be your formatted years
#Rotate tick labels 90 degrees
for label in ax.get_xticklabels():
label.set_rotation(90)
# you only need to do this once
fig.tight_layout()
fig.subplots_adjust(right=0.925)
fig.savefig('%s\\run.pdf' % outDir)
Also see the demo and the docs set_xticks and set_xticklabels
You can use the ax.set_ticklabels() function to set the labels.
Example:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot([1, 2, 3, 4], [10, 20, 25, 30])
ax.xaxis.set_ticklabels(["foo" , "bar", "ouch"])
plt.show()
So, just add the transformation that you need, and create the labels list.
maybe something like this:
range = 47
yearList = [1967, 1968,..., last year]
range_of_years = map(lambda x: range(year,year + range), yearList)
for i in range(len(axis_list)):
axis_list[i].xaxis.set_ticklabels(years_list[i])

Categories

Resources