Editing Radar Charts for Labeling and Axis Limits - python-2.7

I have been playing with the radar chart concept for visualizing percentage-based metrics. I have followed sample code but am having trouble with a few items. Can anyone help me with changing the labels from the default degree values to something else? I also want to set the x-axis minimum to 0.9, but struggled a bit.
Any help or resources are helpful. If there is a more efficient way to solve them, I am open to starting over again.
import numpy as np
import matplotlib.pyplot as plt
availability_array = np.array([.95, .9, .99, .97, 1]) #sample inverter uptime availability numbers using site with 5 inverters
# Compute pie slices
theta = np.linspace(0.0, 2 * np.pi, len(availability_array), endpoint=False)
values = availability_array #values that are graphed
width = 1 #increase/decrease width of each bar
ax = plt.subplot(111, projection='polar') #.set_xticklabels(['N', '', 'W', '', 'S', '', 'E', '']) #111 means 1x1 grid subplot starting in cell 1
bars = ax.bar(theta, values, width=width, bottom=0.0)
# Coloring
for r, bar in zip(values, bars):
bar.set_facecolor(plt.cm.viridis(r / 1))
bar.set_alpha(0.4) #transparency of the bars
plt.show()

As you've already shown in your comments, labels around the circle are xticklabels and labels along the radius are yticklabels, i.e. y-axis is along the radius. Therefore, I think you meant to "set the y-axis minimum to 0.9".
As you would do with regular plot, you can use set_xticks in combine with set_xticklabels to change "the labels from the default degree values to something else". For example:
ax.set_xticks([np.pi/4, np.pi*3/4])
ax.set_xticklabels(['NE', 'NW'])
To "set the y-axis minimum to 0.9", you can use set_ylim like this:
ax.set_ylim(0.9, 1)

Related

How to fully delete plots from subplot and properly resize?

I am trying to create a corner plot for an upcoming paper, but I'm running into difficulty. I am creating an N x N array of subplots (currently, N = 6) and then deleting a bit over half of them. The issue is that the figure doesn't seem to resize itself after I delete the extraneous subplots, so when I later add a legend using a dummy subplot, it exists in the area where a full row and column of deleted subplots were, thus enlarging the figure. I've been working on this for several hours now and haven't found a solution. Here is the MWE:
import matplotlib.pyplot as plt
%matplotlib notebook
n_char = 8
# Set up the main figure.
fig, ax = plt.subplots(n_char, n_char, figsize=(n_char, n_char))
# Get rid of the axis labels unless it's on the left-most column or bottom-most row.
for i in range(0, n_char):
# For each row, loop over each column.
for j in range(0, n_char):
# If the plot isn't in the bottom-most row, get rid of the x-axis tick labels.
if i != n_char - 1:
ax[i, j].set_xticklabels([])
# If the plot isn't in the left-most column, get rid of the y-axis tick labels.
if j != 0:
ax[i, j].set_yticklabels([])
# Remove the plots that are repetitive or boring (plotting against the same characteristic).
for i in range(0, n_char):
# For each row, loop over each column.
for j in range(0, n_char):
# Delete the offending axes.
if j >= i:
ax[i, j].remove()
# Set the spacing between the plots to a much smaller value.
fig.subplots_adjust(hspace=0.00, wspace=0.00)
# Create a big plot for the legend. Have the frame hidden.
fig.add_subplot(111, frameon=False, xticks=[], yticks=[], xticklabels=[], yticklabels=[])
# Create some dummy data to serve as the source of the legend.
plt.scatter([10], [10], color="k", s=5, zorder=2, label="Targets")
# Set the x-axis limits such that the dummy data point is invisible.
fig.gca().set_xlim(-1, 1)
# Add the legend to the plot. Have it located in the upper right.
plt.legend(scatterpoints=1, loc="upper right", fontsize=5)
# Save the final plot.
fig.savefig("./../Code Output/Other Plots/Corner_Plot_Test.png", bbox_inches="tight", dpi=500)
I have looked at many different questions here on Stack Overflow. The two most promising candidates was this one, but I found the solution wasn't quite workable due to the large number of plots (and, to be frank, I didn't fully understand the solution). I thought that the first answer in this one might also work, as I thought it was a sizing issue (i.e. the figure wasn't resizing, so creating a new subplot was creating one the size of the original figure), but all it did was resize the entire figure, so that didn't work either.
To help, I will also include an image. I took the output of the code above and edited it to show what I want:
I should add that if I don't add a subplot, the output is as I expected (i.e. it's the proper size), so the issue comes in when adding the subplot, i.e. the line fig.add_subplot(111, frameon=False, xticks=[], yticks=[], xticklabels=[], yticklabels=[]).
The use of GridSpec may help.
GridSpec is used to specify array of axes to plot. You can set widths for columns and heights for rows as ratios in the option. The unneeded row should have very small height ratio, while unneeded column very small width ratio.
Here is the runnable code and output plot:-
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
#import numpy as np
fig = plt.figure(figsize=(8, 8))
nn = 6
# will create gridspec of 6 rows, 6 columns
# 1st row will occupy v small heights
# last column will occupy v small widths
sm = 0.01 # the v small width/height
wh = (1.-sm)/(nn-1.) # useful width/height
gs = gridspec.GridSpec(nn, nn, width_ratios=[*[wh]*(nn-1), sm], \
height_ratios= [sm, *[wh]*(nn-1)])
cols, rows = nn, nn
ax = [[0 for i in range(cols)] for j in range(rows)]
for ea in range(nn):
for eb in range(nn):
ax[ea][eb] = fig.add_subplot(gs[ea, eb])
ax[ea][eb].set_xticklabels([])
ax[ea][eb].set_yticklabels([])
if eb>=ea:
ax[ea][eb].remove()
# plot data on some axes
# note that axes on the first row (index=0) are gone
ax[2][0].plot([2,5,3,7])
ax[4][2].plot([2,3,7])
# make legend in upper-right axes (GridSpec's first row, last column)
# first index: 0
# second index: nn-1
rx, cx = 0, nn-1
ax[rx][cx] = fig.add_subplot(gs[rx,cx])
hdl = ax[rx][cx].scatter([10], [10], color="k", s=5, zorder=2, label="Targets")
ax[rx][cx].set_axis_off()
#ax[rx][cx].set_visible(True) # already True
ax[rx][cx].set_xticklabels([])
ax[rx][cx].set_yticklabels([])
# plot legend
plt.legend(bbox_to_anchor=(1.0, 1.0), loc='upper right', borderaxespad=0.)
fig.subplots_adjust(hspace=0.00, wspace=0.00)
plt.show

How can I remove the negative sign from y tick labels in matplotlib.pyplot figure?

I am using the matplotlib.pylot module to generate thousands of figures that all deal with a value called "Total Vertical Depth(TVD)". The data that these values come from are all negative numbers but the industry standard is to display them as positive (I.E. distance from zero / absolute value). My y axis is used to display the numbers and of course uses the actual value (negative) to label the axis ticks. I do not want to change the values, but am wondering how to access the text elements and just remove the negative symbols from each value(shown in red circles on the image).
Several iterations of code after diving into the matplotlib documentation has gotten me to the following code, but I am still getting an error.
locs, labels = plt.yticks()
newLabels = []
for lbl in labels:
newLabels.append((lbl[0], lbl[1], str(float(str(lbl[2])) * -1)))
plt.yticks(locs, newLabels)
It appears that some of the strings in the "labels" list are empty and therefore the cast isn't working correctly, but I don't understand how it has any empty values if the yticks() method is retrieving the current tick configuration.
#SiHA points out that if we change the data then the order of labels on the y-axis will be reversed. So we can use a ticker formatter to just change the labels without changing the data as shown in the example below:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
#ticker.FuncFormatter
def major_formatter(x, pos):
label = str(-x) if x < 0 else str(x)
return label
y = np.linspace(-3000,-1000,2001)
fig, ax = plt.subplots()
ax.plot(y)
ax.yaxis.set_major_formatter(major_formatter)
plt.show()
This gives me the following plot, notice the order of y-axis labels.
Edit:
based on the Amit's great answer, here's the solution if you want to edit the data instead of the tick formatter:
import matplotlib.pyplot as plt
import numpy as np
y = np.linspace(-3000,-1000,2001)
fig, ax = plt.subplots()
ax.plot(-y) # invert y-values of the data
ax.invert_yaxis() # invert the axis so that larger values are displayed at the bottom
plt.show()

Matplotlib Subplot - Unexpected Y axis ticks

I am working with matplotlib subplots. This is the skeleton of my code:
import matplotlib.pyplot as plt
from matplotlib import gridspec
plt.close('all')
f, axarr = plt.subplots(2, sharex=True,)
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
axarr[0] = plt.subplot(gs[0])
axarr[1] = plt.subplot(gs[1])
axarr[0].set_ylim([-10,10])
axarr[1].set_ylim([-1,1])
plt.tight_layout()
f.subplots_adjust(hspace=0)
plt.show()
This is the output that I get from this code.
As one can see, in the left y-axis, I get ytick labels which overlap on top of each other and 'weird' y-axis tick labels (0) in the y-axis on the right hand side. How can I solve this? I will be thankful to have help here.
Those are the x labels of the upper subplot which are only partially hidden by the lower subplot. Turn them off if you like,
axarr[0].set_xticklabels([])
In order for the ticklabels not to overlap you may change the ylimits of the axes,
axarr[0].set_ylim([-10.5,10])
axarr[1].set_ylim([-1,1.2])

Python Matplotlib creating a custom colour scale

I have created a map of precipitation levels in a region based on precipitation data from NetCDF files. I would like to add a custom scale such that if precipitation is less than 800mm it would be one colour, 800-1000mm another, etc. Similar to the map found here: http://www.metmalawi.com/climate/climate.php
At the moment I am using a gradient scale but it isn't showing the detail I need. This is the code for the plot at the moment (where 'Average' is my data that I have already formatted).
#load color palette
colourA = mpl_cm.get_cmap('BuPu')
#plot map with physical features
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS)
ax.add_feature(cartopy.feature.LAKES, alpha=0.5)
ax.add_feature(cartopy.feature.RIVERS)
#set map boundary
ax.set_extent([32.5, 36., -9, -17])
#set axis tick marks
ax.set_xticks([33, 34, 35])
ax.set_yticks([-10, -12, -14, -16])
lon_formatter = LongitudeFormatter(zero_direction_label=True)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
#plot data and set colour range
plot = iplt.contourf(Average, cmap=colourA, levels=np.arange(0,15500,500), extend='both')
#add colour bar index and a label
plt.colorbar(plot, label='mm per year')
#give map a title
plt.title('Pr 1990-2008 - Average_ERAINT ', fontsize=10)
#save the image of the graph and include full legend
plt.savefig('ERAINT_Average_Pr_MAP_Annual', bbox_inches='tight')
plt.show()
Anyone know how I can do this?
Thank you!
This is a matplotlib question disguised as an Iris question as the issue has appeared via Iris plotting routines, but to answer this we need only a couple of matplotlib commands. As such, I'm basing this answer on this matplotlib gallery example. These are levels (containing values for the upper bound of each contour) and colors (specifying the colours to shade each contour). It's best if there are the same number of levels and colours.
To demonstrate this, I put the following example together. Given that there's no sample data provided, I made my own trigonometric data. The levels are based on the trigonometric data values, so do not reflect the levels required in the question, but could be changed to the original levels. The colours used are the hex values of the levels specified by image in the link in the question.
The code:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-25, 25)
y = np.arange(-20, 20)
x2d, y2d = np.meshgrid(x, y)
vals = (3 * np.cos(x2d)) + (2 * np.sin(y2d))
colours = ['#bf8046', '#df9f24', '#e0de30', '#c1de2d', '#1ebf82',
'#23de27', '#1dbe20', '#11807f', '#24607f', '#22427e']
levels = range(-5, 6)
plt.contourf(vals, levels=levels, colors=colours)
plt.colorbar()
plt.show()
The produced image:
Colours could also be selected from a colormap (one way of doing this is shown in this StackOverflow answer). There are also other ways, including in the matplotlib gallery example linked above. Given, though, that the sample map linked in the question had specific colours I chose to use those colours directly.

Bar graph for male and female born on particular date/time

I need to draw a bar graph for the values:
male=('2', '1', '2', '6', '6', '1') # list may increase
time=('Tue_Aug_13_04:37:40_2013', 'Mon_Jul__1_02:33:11_2013','Tue_Aug_13_04:37:40_2013', 'Thu_Jul__4_01:53:32_2013', 'Mon_Jul__1_10:05:55_2013','Mon_Jul__1_04:15:25_2013')# list may increase
female=(16, 11, 16, 12, 12, 11) # list may increase
Male in green colour, female in red colour as the image attached below:
The code which I tried:
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse, Polygon
fig = plt.figure()
ax1 = fig.add_subplot(131)
ax1.bar(male, color='red', edgecolor='black')
ax1.bar(bottom=range(female), color='blue', edgecolor='black')
ax1.set_xticks(time)
plt.show()
What modifications do I need to make in order to draw the bar graph as shown in the image attached for my values?
1.) I strongly suggest that you familiarize yourself with the python syntax:
What's the difference between lists enclosed by square brackets and parentheses?
What's the difference between '2' and 2?
2.) Make use of the matplotlib documentation to figure out the correct syntaxt for the plot commands you are using.
3.) In this particular case: To get you going, change your data to:
male=[2, 1, 2, 6, 6, 1] # list may increase
time=['Tue_Aug_13_04:37:40_2013', 'Mon_Jul__1_02:33:11_2013','Tue_Aug_13_04:37:40_2013', 'Thu_Jul__4_01:53:32_2013', 'Mon_Jul__1_10:05:55_2013','Mon_Jul__1_04:15:25_2013']# list may increase
female=[16, 11, 16, 12, 12, 11] # list may increase
Please examine carefully what has changed.
4.) The bar command you try to call has not enough input arguments. With the changed data from above, try this:
ax1.bar(range(len(time)),male,width=0.5, color='red', edgecolor='black')
ax1.bar(range(len(time)),female,width=0.5,bottom=male,color='blue', edgecolor='black')
What has changed?
you need the following inputs: left, height, width=0.8
you had only one of those
due to the fact that your dates are given as strings, you need a generic counter for the x-axis, hence the range(len(time)) to provide as many tics as there are entries in time.
now, you specify the height according to the values in male and female - none of which should be strings!
define a width
in your case, you want the bars to be stacked - therefore, specify the first set of values as bottom for the second
4.) Because time is made up of strings, you cannot use it for the ticks. Instead, try:
ax1.set_xticklabels(time,rotation=90)
Here, you use the strings from time as tick-labels. The rotation=90 is a nice feature so that the long strings do not overlap.
5.) If the labels are cut off by the plot window, try this:
plt.tight_layout()
plt.show()
This should get you back on track.
Good key words for a web-search inlcude:
matplotlib stacked bar
matplotlib tick labels rotation
matplotlib ticks date