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).
Related
I'm new to python.
I'm using python 2.7 and i'm using pandas plot to make a BarChart . here 's my code
my_colors = list(islice(cycle(['#AB82FB','#9A958F','#0131CC', '#EA3C00', '#22DC00','#CCC201','#01BECC','#CC6F01','#8F959A','#02E7D1','#01FF00','#FDF505']), None, len(df)))
df = pd.read_csv(r"/path",sep=';')
data3=df[['DAY_DEPOSE','CHEMIN_REPERTOIRE_SOURCE','TPS_ATTENTE_HOURE']]
tab4=pd.crosstab(index = data3['DAY_DEPOSE'],columns=data3['CHEMIN_REPERTOIRE_SOURCE'],values=data3['TPS_ATTENTE_HOURE'],margins=False,aggfunc='mean')
tab4=tab4[['source_fichier','source_xml']]
fig4=plt.figure()
tab4.plot(kind='bar', color=my_colors,figsize=(20,18))
plt.yticks(np.arange(0, 50, 2))
plt.grid(axis='y')
plt.savefig(r'/path/figure4.png')
and here is my barChart
now what i wanna add is : when a value exeed from the maximum value of axis(in our cas '50') , i want it to stop at that value and have a label that indicate its value , for exemple:
Can we do it , would please help me
Consider the following example.
import pandas as pd
import random
import numpy as np
df = pd.DataFrame({'x': [x for x in range(30)],
'y': [random.randint(0, 60) for x in range(30)],
})
You can recreate a similar plot to the one you have with the following.
plt.figure(figsize=(5,5))
plt.yticks(np.arange(0, 55, 5))
plt.bar(df.x.values, df.y.values)
plt.show()
You can limit the y-axis, and add labels using something like the following.
plt.figure(figsize=(5,5))
plt.yticks(np.arange(0, 55, 5))
plt.bar(df.x.values, np.where(df.y.values > 50, 50, df.y.values))
plt.ylim(0, 55)
for i in range(len(df.y.values)):
if df.y.values[i] >= 50:
plt.annotate(str(df.y.values[i]), xy=(df.x.values[i], np.where(df.y.values > 50, 50, df.y.values)[i]), ha='center', va='bottom')
plt.show()
I've recently started using the dark chesterish theme from dunovank, and I
love how good a simple pandas.DataFrame.plot() looks like out of the box:
Snippet 1:
# Theme from dunovank, exclude if not installed:
from jupyterthemes import jtplot
jtplot.style()
# snippet from pandas docs:
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
ax = ts.plot()
Output 1:
But I'd like to add an alternating background color (seems to be all the rage with big news agencies). The post How can I set the background color on specific areas of a pyplot figure? gives a good description of how you can do it. And it's really easy for numeric x-values:
Snippet 2:
# imports
import pandas as pd
import numpy as np
from jupyterthemes import jtplot
# Sample data
np.random.seed(123)
rows = 50
dfx = pd.DataFrame(np.random.randint(90,110,size=(rows, 1)), columns=['Variable Y'])
dfy = pd.DataFrame(np.random.randint(25,68,size=(rows, 1)), columns=['Variable X'])
df = pd.concat([dfx,dfy], axis = 1)
jtplot.style()
ax = df.plot()
for i in range(0, 60, 20):
ax.axvspan(i, i+10, facecolor='lightgrey', alpha=0.025)
Output 2:
But it gets a lot messier (for me at least) when the x-axis is of a time or date format. And that's because the axis in my two examples goes from this
# in:
ax.lines[0].get_data()
# out:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
dtype=int64)
To this (abbreviated):
# in:
ts.plot().lines[0].get_data()
# out:
.
.
Period('2002-09-15', 'D'), Period('2002-09-16', 'D'),
Period('2002-09-17', 'D'), Period('2002-09-18', 'D'),
Period('2002-09-19', 'D'), Period('2002-09-20', 'D'),
Period('2002-09-21', 'D'), Period('2002-09-22', 'D'),
Period('2002-09-23', 'D'), Period('2002-09-24', 'D'),
Period('2002-09-25', 'D'), Period('2002-09-26', 'D')], dtype=object)
ts.plot().lines[0].get_data() returns the data on the x-axis. But is there a way to find out where matplotlib renders the vertical lines for each 'Jan' observation, so I can more easily find decent intervals for the alternating black and grey background color?
Thank you for any suggestions!
Edit - Or is there a theme?
Or does anyone know if there exists a theme somewhere that is free to use?
I've checked all matplotlib themes import matplotlib.pyplot as plt; print(plt.style.available) and Seaborn, but with no success.
Edit 2 - Suggested solution from ImportanceOfBeingErnest with the chesterish theme activated:
In my humble opinion, this is a perfect setup for a time series chart (could maybe drop the splines though)
Gridlines are by default shown at the positions of the major ticks. You can get those ticks via ax.get_xticks(). The problem will be that it is not guaranteed that the edges of the plot coincide with those ticks, in fact they are most often dissimilar. So in order to have a consistent shading over the range of the axes, the first shade should start at the edge of the plot and end at the first gridline, then the following shades can go in between gridlines, up to the last, which will again be between the last gridline and the edge of the axes.
Another problem is that the limits of the plot and hence the automatically generated gridlines may change over the lifetime of the plot, e.g. because you decide to have different limits or zoom or pan the plot. So ideally one would recreate the shading each time the axis limits change. This is what the following does:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# time series
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
# numeric series
#ts = pd.Series(np.random.randn(1000),index=np.linspace(25,800,1000)).cumsum()
ax = ts.plot(x_compat=True)
ax.grid()
class GridShader():
def __init__(self, ax, first=True, **kwargs):
self.spans = []
self.sf = first
self.ax = ax
self.kw = kwargs
self.ax.autoscale(False, axis="x")
self.cid = self.ax.callbacks.connect('xlim_changed', self.shade)
self.shade()
def clear(self):
for span in self.spans:
try:
span.remove()
except:
pass
def shade(self, evt=None):
self.clear()
xticks = self.ax.get_xticks()
xlim = self.ax.get_xlim()
xticks = xticks[(xticks > xlim[0]) & (xticks < xlim[-1])]
locs = np.concatenate(([[xlim[0]], xticks, [xlim[-1]]]))
start = locs[1-int(self.sf)::2]
end = locs[2-int(self.sf)::2]
for s, e in zip(start, end):
self.spans.append(self.ax.axvspan(s, e, zorder=0, **self.kw))
gs = GridShader(ax, facecolor="lightgrey", first=False, alpha=0.7)
plt.show()
Use an axis vertical span with datetime values for the x-values:
from jupyterthemes import jtplot
import pandas as pd
import numpy as np
from datetime import datetime
jtplot.style()
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
ax = ts.plot()
# or an appropriate for-loop
ax.axvspan(datetime(1999, 12, 15), datetime(2000, 1, 15), facecolor='red', alpha=0.25)
ax.axvspan(datetime(2000, 12, 15), datetime(2001, 1, 15), facecolor='red', alpha=0.25)
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
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])
I'm trying to produce a plot which uses the same colorscale as the Met Office, so I can easily compare my plots to theirs. An example of theirs is at Here
My current closest effort is here:
Here
I appreciate my code is messy - I couldn't find a way to set a color for values above a certain threshold (otherwise it goes white),hence the loop.
I would upload the NetCDF File but I haven't got a high enough rep to do this.
Many, many thanks in advance for any help.
My code for plotting is shown below;
from Scientific.IO.NetCDF import NetCDFFile
from mpl_toolkits.basemap import Basemap
from matplotlib import pyplot as plt
import numpy as np
myfile = NetCDFFile('ERA_Dec_89-94.nc', 'r')
Lat = NetCDFFile('/home/james/Documents/Lat_Lon_NC_Files/latitudes_d02.nc','r')
Long = NetCDFFile('/home/james/Documents/Lat_Lon_NC_Files/longitudes_d02.nc','r')
XLAT = Lat.variables['XLAT'][:]
XLONG = Long.variables['XLONG'][:]
ERA_Data = myfile.variables['Monthlyrain'][:]
plot = np.zeros([1000,1730])
plot[:,:] = np.average(ERA_Data[:,:,:],axis=0)
m = Basemap(projection='merc',resolution='f',llcrnrlat=49,llcrnrlon=-11,urcrnrlat=61,urcrnrlon=3)
m.drawparallels(np.arange(-90., 91., 5.), labels=[1,0,0,0], fontsize=11)
m.drawmeridians(np.arange(-180., 181., 5.), labels=[0,0,0,1], fontsize=11)
m.drawcoastlines()
X, Y = m(XLONG, XLAT)
for i in range(0,1729):
for j in range(0,999):
if plot[j,i] >250:
plot[j,i] = 250.001
if plot[j,i] < 40:
plot[j,i] = 40
scale = [40,40.001,60,80,100,125,150,200,250, 250.001]
cs = m.contourf(X,Y,plot,scale, cmap='PuOr')
cbar = m.colorbar(cs, ticks= [40.0005,50,70,90,112.5,137.5,175,225,250.0005])
cbar.set_ticklabels(['<40','40-60', '60-80', '80-100', '100-125', '125-150', '150-200', '200-250', '>250'])
plt.title('Some Title')
cbar.set_label('Monthly average rainfall (mm)')
print "Finished"
plt.show()
If the issue is simply the colormap, you can pick the RGB components of the colors off your screen and turn them into a ListedColormap, mapped to the boundaries of the rainfall in the chart you give as an example. For example,
bounds = [0, 40, 60, 80, 100, 125, 150, 200, 250, 1000]
rgblist = [(51,0,0), (102,51,0), (153,102,51), (204,153,102), (255, 255, 255),
(204,204,255), (153,153,255), (51,102,255), (0,0,153)]
clist = [[c/255 for c in rgb] for rgb in rgblist]
from matplotlib import colors
cmap = colors.ListedColormap(clist)
norm = colors.BoundaryNorm(bounds, cmap.N)
ax.imshow(arr, cmap=cmap, norm=norm)
plt.show()
The first part (getting the colors right) was already answered. In order to restrict the values to a certain range you have several options.
Use cmap.set_over and cmap.set_under to set out-of-bounds colors, as described here
use np.clip instead of the loop to restrict the values to a certian range:
plot = np.clip(plot, 40, 250)