I'm trying to plot contourf of 7 clusters with min value in array of 0 and max of 6. However, when I plot it, there is an extra step size in the plot (the color pink outlining the brown cluster.Even though the colorbar labeled the pink as 5, it isn't supposed to be there so I'm not sure where it came from since label 5 is for the brown cluster and so on.
enter image description here
plt.clf() #clear figure before
fig=plt.figure(figsize=(10,8))
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=100.0, globe=None))
ax.set_extent([96,105,1,8]) # lon_left, lon_right, lat_below, lat_upper
ax.gridlines(linewidths=0.01, draw_labels=True, alpha= 0.3)
ax.xlocator = mticker.FixedLocator(np.arange(96.,105.,0.5))
ax.ylocator = mticker.FixedLocator(np.arange(1.,8.,0.5))
clevs = np.arange(0,7,1)
cs = ax.contourf(coords[0], coords[1], code_recons[0], vmin=0, vmax=6, cmap="Accent", transform=ccrs.PlateCarree())
ax.coastlines("50m") # avail:110m, 50m, 10m..... '10m' is better resolution than default
cb = plt.colorbar(cs)
n_clusters=7
tick_locs = (np.arange(n_clusters) + 0.5)*(n_clusters-1)/n_clusters
cb.set_ticks(tick_locs)
cb.set_ticklabels(np.arange(n_clusters))
plt.show()
Any help is deeply appreciated.
Related
I want to plot the average intensity of Sea Surface Temperature map using cartopy.
Here is the result:
I want to clarify that I want to fill ocean map with average intensity and the land map with face color setting.
%%time
# figure
fig = plt.figure(figsize=(15, 20))
fig, ax = plt.subplots()
# cartopy
ax = plt.axes(projection=ccrs.Mercator())
ax.coastlines()
gl = ax.gridlines(draw_labels=True, alpha=0.6)
ax.add_feature(cfeature.LAND, facecolor='0.5', edgecolor='black')
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlocator = mticker.FixedLocator(np.arange(94,145, 10))
gl.ylocator = mticker.FixedLocator(np.arange(-20,8, 10))
gl.xlabel_style = {'size':8, 'color':'k'}
gl.ylabel_style = {'size':8, 'color':'k'}
extent = [94, 145, -20, 8]
ax.set_extent(extent)
dnc_gridded.Average_Intensity.plot(ax=ax,
transform=ccrs.PlateCarree(),
colors="Reds",
extend='both',
cbar_kwargs={'spacing': 'proportional',
'label':'Deg C'})
The figure size is not changing even though I tried several size sets. I couldn't make it bigger.
Regarding cfeature.land, I set the facecolor at 0.5 but it didn't seem to work. It works at the extended plotted area. I notice that 'Average Intensity' variable is plotted over the land map instead of facecolor. I need to specify the levels or contour if I want to differ it from ocean.
The real problem is I have several variables and all of them has inconsistent land colour because I need to re-write each level of color.
Additional problem, how to plot (degree) symbol? In this code, I simply make Deg C because I cannot make the superscript of the degree symbol
Does anyone have any ideas, on how to plot this map properly?
Calling fig, ax = plt.subplots() overwrites the previously declared fig. You need to create fig only once and specify the figsize in that call, i.e. fig, ax = plt.subplots(figsize=(15, 20))
Not clear what you're asking here, please clarrify. If you want cfeature.land to plot on top of your contourf, set the zorder
A simple search for "matplotlib degree symbol" brings up this SO question with the exact answer you need
I want to plot a Seaborn swarmplot of 'LapTimeSeconds' vs 'Driver' and I would like each swarm (driver) to be plotted in the corresponding colour in the 'Colour' column, I would also like for the edge colour of each of the markers to be the given 'Compound_colour'
I have a dataframe, df, looking like:
Driver Colour LapNumber LapTimeSeconds Compound Compound_colour
HAM #00d2be 2 91.647 MEDIUM #ffd300
HAM #00d2be 5 91.261 MEDIUM #ffd300
HAM #00d2be 8 91.082 SOFT #FF3333
VER #0600ef 3 91.842 MEDIUM #ffd300
VER #0600ef 6 91.906 MEDIUM #ffd300
NOR #ff8700 10 90.942 SOFT #FF3333
Here is some code that I have currently.
sns.set_palette(df['Colour'].unique().tolist())
ax = sns.boxplot(x="Driver", y="LapTimeSeconds", data=df, width = 0.8, color = 'white')
ax = sns.swarmplot(x="Driver", y="LapTimeSeconds", data=df, size = 9, linewidth=1)
which gives a plot looking like this plot
However, I would like the edgecolor of each marker to be the corresponding 'Compound_colour', for example where the compound is 'medium' I want the edgecolor to be '#ffd300' (yellow) and where the compound is 'soft' I want the edgecolor to be '#FF3333' (red) .
This is similar to what I am aiming for. Is there a way to do this?
To customize each marker in swarmplot, set the specified color as it can be obtained from the collection. Also, to change the color of each boxplot, change the color of the line object since it is contained in ax.artists.
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib import colors
sns.set_palette(df['Colour'].unique().tolist())
fig,ax = plt.subplots()
ax =sns.boxplot(x="Driver", y="LapTimeSeconds", data=df, width=0.8, color='white')
swarm = sns.swarmplot(x="Driver", y="LapTimeSeconds", data=df, size=9, linewidth=1)
swarm.collections[0].set_ec(colors.to_rgba('#ffd300', 1.0))
swarm.collections[1].set_ec(colors.to_rgba('#FF3333', 1.0))
swarm.collections[2].set_ec(colors.to_rgba('#FF3333', 1.0))
swarm.collections[0].set_edgecolors(['#FF3333','#ffd300','#ffd300'])
colors = ['#00d2be','#0600ef','#ff8700']
for i, artist in enumerate(ax.artists):
# print(i, artist)
artist.set_edgecolor(colors[i])
for j in range(i*6,i*6+6):
line = ax.lines[j]
line.set_color(colors[i])
line.set_mfc(colors[i])
line.set_mec(colors[i])
plt.show()
I believe I am 90% of the way to solving this issue. As can be seen on the attached image, I am trying to create a nested donut chart based on two groups (rem and deep), the values have been plotted to show the proportion (in relation to 100%) a user is achieving. I want to display this in the "apple health ring style", in order for this to be effective, I want to highlight the two "types" (rem and deep) using two different colors. Within the image you can see the DF, the code applied to generate the existing view and the output. To summarize, I want to;
Assign a set color to "rem", and a different one for "deep"
Remove the axis labels and tick marks
Ideally (although I can probably do this), better format the labels (in some way).
Output to image file with black background
import re
# create donut plots
my_dpi=150
plt.figure(figsize=(1500/my_dpi, 900/my_dpi), dpi=my_dpi)
startingRadius = 0.7 + (0.3* (len(Ian_MitchellRD)-1))
for index, row in Ian_MitchellRD.iterrows():
scenario = row["index"]
percentage = row["Ian Mitchell"]
textLabel = scenario + ': ' + percentage+ '%'
print(startingRadius)
percentage = int(re.search(r'\d+', percentage).group())
remainingPie = 100 - percentage
donut_sizes = [remainingPie, percentage]
#colors = ['#FDFEFE','#AED6F1','#5cdb6f','#AED6F1']
plt.title('Proportion of Recommended Sleep Type being Achieved')
plt.text(0.05, startingRadius - 0.20, textLabel, horizontalalignment='left', verticalalignment='center', color='black')
plt.pie(donut_sizes, radius=startingRadius, startangle=90, colors=colors, frame=True,
wedgeprops={"edgecolor": "white", 'linewidth': 2}, )
startingRadius-=0.3
# equal ensures pie chart is drawn as a circle (equal aspect ratio)
plt.axis('equal')
# create circle and place onto pie chart
circle = plt.Circle(xy=(0, 0), radius=0.35, facecolor='white')
plt.gca().add_artist(circle)
plt.show()
See image of what the code current generates:
UPDATE:
I amended the code per the recommendation to look into the example suggested, code is now;
import matplotlib.lines as mlines
fig, ax = plt.subplots()
ax.axis('equal')
width = 0.25
fig.patch.set_facecolor('black')
fig.set_size_inches(10,10)
data_1 = Ian_MitchellRD.iloc[0]['Ian Mitchell']
data_2 = Ian_MitchellRD.iloc[1]['Ian Mitchell']
remainingPie_1 = 100 - data_1
remainingPie_2 = 100 - data_2
donut_sizes_1 = [remainingPie_1, data_1]
donut_sizes_2 = [remainingPie_2, data_2]
pie, _ = ax.pie(donut_sizes_1, radius=1, colors=['black','lightgreen'],startangle=90)
plt.setp( pie, width=width, edgecolor='black')
pie2, _ = ax.pie(donut_sizes_2, radius=1-width,startangle=90, colors=['black','pink'])
plt.setp( pie2, width=width, edgecolor='black')
plt.title("Ian Mitchell - Average % of REM + Deep Sleep vs Recommended", fontfamily='Consolas', size=16, color='white')
#setting up the legend
greenbar= mlines.Line2D([], [], color='lightgreen', marker='s', linestyle='None',
markersize=10, label='REM Sleep')
pinkbar = mlines.Line2D([], [], color='pink', marker='s', linestyle='None',
markersize=10, label='Deep Sleep')
plt.legend(handles=[greenbar, pinkbar],prop={'size': 12}, loc='lower right')
plt.show()
generating the following;
I would really appreciate guidance on adding labels either, directly on the relevant sections of the chart - i.e. the green chart has a label of around 94%, and the pink segment around 55%.
Thanks,
New Version
I am plotting a pie chart making background in the png image looks transparent. How can I make the center circle also looks transparent instead of the white color?
import matplotlib.pyplot as plt
# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = 'Correct', 'Wrong'
sizes = [20, 80]
fig1, ax1 = plt.subplots()
ax1.pie(sizes,colors=['green','red'], labels=labels,autopct='%1.1f%%',
shadow=True, startangle=90)
centre_circle = plt.Circle((0,0),0.75,edgecolor='black',
facecolor='white',fill=True,linewidth=0.25)
fig1 = plt.gcf()
fig1.gca().add_artist(centre_circle)
ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
fig1.savefig('foo.png', transparent=True)
The way you create the white middle part in the above code is by obfuscating the center of the pie by a circle. This can of course not procude a transparent interior.
A solution to this would also be found in the more sophisticated question Double donut chart in matplotlib. Let me go into detail:
In order to produce a true donut chart with a hole in the middle, one would need to cut the wedges such that they become partial rings. Fortunately, matplotlib provides the tools to do so. A pie chart consists of several wedges.
From the
matplotlib.patches.Wedge documentation we learn
class matplotlib.patches.Wedge(center, r, theta1, theta2, width=None, **kwargs)
Wedge shaped patch.
[...] If width is given, then a partial wedge is drawn from inner radius r - width to outer radius r.
In order to give set the width to all wedges, an easy method is to use plt.setp
wedges, _ = ax.pie([20,80], ...)
plt.setp( wedges, width=0.25)
Complete example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
fig.set_facecolor("#fff9c9") # set yellow background color to see effect
wedges, text, autotext = ax.pie([25, 40], colors=['limegreen','crimson'],
labels=['Correct', 'Wrong'], autopct='%1.1f%%')
plt.setp( wedges, width=0.25)
ax.set_aspect("equal")
# the produced png will have a transparent background
plt.savefig(__file__+".png", transparent=True)
plt.show()
The following would be a way to tackle the problem if the Wedge did not have a width argument.
Since the pie chart is centered at (0,0), copying the outer path coordinates, reverting them and multiplying by some number smaller 1 (called r for radius in below code), gives the coordinates of the inner ring. Joining those two list of coordinates and taking care of the proper path codes allows to create a ring shape as desired.
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import numpy as np
def cutwedge(wedge, r=0.8):
path = wedge.get_path()
verts = path.vertices[:-3]
codes = path.codes[:-3]
new_verts = np.vstack((verts , verts[::-1]*r, verts[0,:]))
new_codes = np.concatenate((codes , codes[::-1], np.array([79])) )
new_codes[len(codes)] = 2
new_path = mpath.Path(new_verts, new_codes)
new_patch = mpatches.PathPatch(new_path)
new_patch.update_from(wedge)
wedge.set_visible(False)
wedge.axes.add_patch(new_patch)
return new_patch
fig, ax = plt.subplots()
fig.set_facecolor("#fff9c9") # set yellow background color to see effect
wedges, text, autotext = ax.pie([25, 75], colors=['limegreen','indigo'],
labels=['Correct', 'Wrong'], autopct='%1.1f%%')
for w in wedges:
cutwedge(w)
# or try cutwedge(w, r=0.4)
ax.set_aspect("equal")
# the produced png will have a transparent background
plt.savefig(__file__+".png", transparent=True)
plt.show()
The problem is that you didnt really make a real donut chart. With this part of the code
centre_circle = plt.Circle((0,0),0.75,edgecolor='black',
facecolor='white',fill=True,linewidth=0.25)
you drew a circle in the middle of a pie chart. The problem is if you make this circle transparent you will once again see the middle of the pie chart. I recommend using a free photo editing program like pixlr to just make it transparent. Unless you can find a way to make a true donut chart which I unfortunantly do not know how to do it.
Similarly to the Double donut chart in matplotlib solution referenced by importanceofbeingearnest, you will need to use plt.setp(pie, width=width) to set the width of your pie chart, which will make it a true donut instead of a pie chart with a solid circle drawn on top.
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
ax1.axis('equal')
# Set the width of the pie slices;
# this is equivalent to (1.0-0.75), or
# (the radius of the pie chart - the radius of the inner circle)
width=0.25
# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = ['Correct', 'Wrong']
sizes = [20., 80.]
# ax1.pie will return three values:
# 1. pie (the dimensions of each wedge of the pie),
# 2. labtext (the coordinates and text for the labels)
# 3. labpct (the coordinates and text of the "XX.X%"" labels)
pie, labtext, labpct = ax1.pie(x=sizes,
labels=labels,
colors=['green','red'],
startangle=90,
shadow=True,
autopct='%1.1f%%'
)
# apply "plt.setp" to set width property
plt.setp(pie, width=width)
# save the figure as transparent
fig1.savefig('foo.png', transparent=True)
i am plotting with matplotlib. the code is the following (zvals has the values)
cmap = mpl.colors.ListedColormap(['darkblue', 'blue', 'lightblue','lightgreen','yellow','gold','orange','darkorange','orangered','red'])
bounds=[0, 10,20,30,40,50,60,70,80,100,200,1000]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
img2 = plt.imshow(zvals,interpolation='nearest',
cmap = cmap,
norm=norm,
origin='lower')
xlocations = na.array(range(30)) + 0.5
xticks(xlocations, [str(x+1) for x in arange(30)], rotation=0, size=5)
gca().xaxis.set_ticks_position('none')
gca().yaxis.set_ticks_position('none')
grid(True)
this results in the following picture:
http://imageshack.us/a/img145/7325/histogrammoverview.png
i would like to move the labels of the xticks (1,2,3,..) to the left a bit, so they are underneath the corresponding color boxes. correspondingly i would also like to move the labels of the yticks (user1 and user2) down a bit so they are displayed correctly. how can this be done?
EDIT: as a matter of fact i could change the following line
xlocations = na.array(range(30)) + 0.5
to
xlocations = na.array(range(30))
then the resulting pictures is like this:
http://imageshack.us/a/img338/7325/histogrammoverview.png
please see that the grid is going "through" the colored boxes, which is not what i want. i'd like the grid to edge the colored boxes as in the above picture. in this version though the labels (1,2,3,...) are placed correctly underneath the boxes. how can i have correctly places labels (underneath the colored boxes) and a grid which is around the colored boxes and not through the middle of the colored boxes.
SOLUTION
this solution works (as suggested by the answer):
periods = 30
xlocations = na.array(range(periods))
xminorlocations = na.array(range(periods))+0.5
xticks(xlocations, [str(x+1) for x in arange(periods)], rotation=0, size=5)
plt.set_xticks(xminorlocations, minor=True)
grid(True, which='minor', linestyle='-')
result: hxxp://imageshack.us/a/img9/7325/histogrammoverview.png
I think that you can manage that by
Setting the major tick locations to the middle of each square.
Setting the minor ticks to the edges of each square.
Setting the grid to show only in the minor ticks.
The grid can be showed only in the minor ticks using
plt.grid(True, which='minor')
I would set the line style to '-' too.