Cartopy fancy box - python

Hello I have been trying to plot data in a Orthographic projection. The data is plotted but I want the box to follow the data limits. Like in this example I am sharing form M_map[enter image description here
Do you have any suggestion?

In the comments under the question, you have several links to the existing answers that match the question. So that, this question is likely a duplicate question. However, none of the answers offers the zebra-pattern border as shown in the sample plot.
I take this opportunity to offer a distinct answer that also plots the map border with zebra pattern line similar to the sample plot.
import cartopy.crs as ccrs
import cartopy
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import matplotlib.patches as patches
# The lat-long projection
noProj = ccrs.PlateCarree(central_longitude=0)
# The projection of the map:
myProj = ccrs.Orthographic(central_longitude=-25, central_latitude=58)
myProj._threshold = myProj._threshold/40. #for higher precision plot
fig = plt.figure(figsize=(8,12))
ax = fig.add_subplot(1, 1, 1, projection=myProj)
# Zebra-border-line segments ...
# four edges on separate lines of code
# 1: lower edge: Left - Right
# 2: Right edge: Bottom - Top
# 3: Upper edge: Right - Left
# 4: Left edge: Top - Bottom
[ax_hdl] = ax.plot(
[
-45, -40, -35, -30, -25, -20, -15, -10, -5,
-5,-5,-5,-5,-5,
-10,-15,-20,-25,-30,-35,-40,-45,
-45, -45, -45, -45, -45
],
[
45, 45, 45, 45, 45, 45, 45, 45, 45,
50, 55, 60, 65, 70,
70,70,70,70,70,70,70,70,
65, 60, 55, 50, 45
],
color='black', linewidth=0.5,
transform=noProj)
tx_path = ax_hdl._get_transformed_path()
path_in_data_coords, _ = tx_path.get_transformed_path_and_affine()
polygon1s = mpath.Path( path_in_data_coords.vertices)
vcode = [1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1] #Path-code
polygon1v = mpath.Path( path_in_data_coords.vertices, vcode)
ax.set_boundary(polygon1s) #masks-out unwanted part of the plot
# Zebra-pattern creation
# The pattern line is created from 2 layers
# lower layer: thicker, black solid line
# top layer: thinner, dashed white line
patch1s = patches.PathPatch(polygon1s, facecolor='none', ec="black", lw=7, zorder=100)
patch1v = patches.PathPatch(polygon1v, facecolor='none', ec="white", lw=6, zorder=101)
ax.add_patch(patch1s)
ax.add_patch(patch1v)
ax.gridlines(draw_labels=True, x_inline=False, y_inline=False)
ax.add_feature(cartopy.feature.OCEAN, linewidth=.3, color='lightblue')
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
ax.title.set_text("Map with zebra border line")
plt.show()

Related

Plotting Log-normal scale in matplotlib

I've got these two lists which are x,y points to be plotted:
microns = [38, 45, 53, 63, 75, 90, 106, 125, 150, 180]
cumulative_dist = [25.037, 32.577, 38.34, 43.427, 51.57,56.99, 62.41,69.537,74.85, 81.927]
The thing is I need to plot them following the scale showed in the image below (more info here), which is a log-normal plot.
How can I get this scale using matplotlib?
I guess I'll need to use matplotlib.scale.FuncScale, but I'm not quite sure how to get there.
After David's insightful comment I've read this page and managed to plot the Figure the way I wanted.
from matplotlib.ticker import ScalarFormatter, AutoLocator
from matplotlib import pyplot
import pandas as pd
import probscale
fig, ax = pyplot.subplots(figsize=(9, 6))
microns = [38, 45, 53, 63, 75, 90, 106, 125, 150, 180]
cumulative_dist = [25.037, 32.577, 38.34, 43.427, 51.57,56.99, 62.41,69.537,74.85, 81.927]
probscale.probplot(pd.Series(microns, index=cumulative_dist), ax=ax, plottype='prob', probax='y', datascale='log',
problabel='Cumulative Distribution (%)',datalabel='Particle Size (μm)',
scatter_kws=dict(marker='.', linestyle='none', markersize=15))
ax.set_xlim(left=28, right=210)
ax.set_ylim(bottom=1, top=99)
ax.set_title('Log Normal Plot')
ax.grid(True, axis='both', which='major')
formatter = ScalarFormatter()
formatter.set_scientific(False)
ax.xaxis.set_major_formatter(formatter)
ax.xaxis.set_minor_formatter(formatter)
ax.xaxis.set_major_locator(AutoLocator())
ax.set_xticks([]) # for major ticks
ax.set_xticks([], minor=True) # for minor ticks
ax.set_xticks(microns)
fig.show()

Overlapping matplot Bar Graphs?

I'm learning matplot in python and I want to make a bar graph with side-by-side bars. My plot is currently overlapping for some reason.
Graph
import numpy as np
import matplotlib.pyplot as plt
n_groups = 7
means_frank = (82, 75, 86, 63, 90, 73 ,88)
means_alex = (91, 92, 80, 73, 83, 91, 71)
means_joe = (72, 42, 50, 33, 63, 34, 54)
fig = plt.figure()
ax = fig.add_subplot(111)
index = np.arange(n_groups)
bar_width = 0.27
opacity = 0.8
rects1 = ax.bar(index,means_frank,bar_width,color='b', label="Frank")
rects2 = ax.bar(index,means_alex,bar_width,color='g', label="Alex")
rects3 = ax.bar(index,means_joe,bar_width,color='r', label="Joe")
plt.ylabel('Scores')
plt.title('Test Scores')
plt.xticks([0, 5, 6], ["Assignments -->", "<-- Midterm", "Final"])
plt.legend()
plt.tight_layout()
plt.show()
How can I make these 3 different plots appear side by side instead of overlapped?
Thanks!
#ImportanceofBeingErnest helped with this link:
https://matplotlib.org/gallery/lines_bars_and_markers/barchart.html
The index argument must be modified to prevent overlap.

Single stacked bar chart with custom gradient coloring

Here's what I came up with by plotting thick line segments.
The coloration is blue, with varying alpha, 0 < alpha < 1.
My workaround doens't work as I'd like because I don't have a legend (I want a legend that shows a gradient of the blue at varying alpha).
Additionally, I've found that matplotlib scales funny. There should be no overlap of the bars, but if I adjust the window size, the gap between the line segments will change.This is the same figure as the earlier one, just after I've resized the figure window with my mouse.
I'm not sure if there's a better way to go about accomplishing this, or if there's a different package I can use.
Here's the snippet of code that I'm using.
import matplotlib.pyplot as plt
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
alpha_list = [-0.8402, -0.6652, 0.0, -0.5106, -0.8074, 0.0]
plt.figure()
for idx,x in enumerate(x1):
plt.plot([x1[idx],x2[idx]],[0,0],color = 'blue',alpha=alpha_list[idx],linewidth =20)
plt.show()
I suppose alpha is just a workaround for using different shades of blue? In that case the Blues colormap can be used instead.
Several lines can be plotted using a LineCollection.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
alpha_list = [-0.8402, -0.6652, 0.0, -0.5106, -0.8074, 0.0]
verts = np.dstack((np.c_[x1, x2], np.zeros((len(x1), 2))))
fig, ax = plt.subplots()
lc = LineCollection(verts, linewidth=40, cmap="Blues_r", array=np.array(alpha_list))
ax.add_collection(lc)
ax.autoscale()
ax.set_ylim(-1,1)
fig.colorbar(lc)
plt.show()
I think a workaround would be to use plt.barh. Here is an example using normalized color maps. Each color gets converted to RGBA before it can be passed to plt.barh.
import matplotlib.pyplot as plt
from matplotlib import colors
import matplotlib.cm as cmx
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
values = range(len(x1))
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
fig, ax = plt.subplots()
for idx, x, y in zip(values,x1, x2):
colorVal = scalarMap.to_rgba(values[idx])
start = x
end = y
width=end-start
ax.barh(y = 0, width = width, left=start, height = 0.1, label = str(idx), color=colorVal)
ax.set_ylim(-.5,0.5)
ax.legend()
which returns:
If you really want to just change the alpha transparency of a single color, you would just have to input alpha_list[idx] for the last element to the RGBA tuple colorVal. For some reason, RGBA did not like negative alpha values, so notice I changed them all to positive
fig, ax = plt.subplots()
alpha_list = [0.8402, 0.6652, 0.01, 0.5106, 0.8074, 0.0]
for idx, x, y in zip(values,x1, x2):
colorVal = (0.0, 0.3, 1.0, alpha_list[idx])
start = x
end = y
width=end-start
ax.barh(y = 0, width = width, left=start, height = 0.1, label = str(idx), color=colorVal)
ax.set_ylim(-.5,0.5)
ax.legend()

Plot a histogram using Python with manual bins

I am trying to plot a histogram using the matplotlib.hist() function.
Below code is not getting the correct histogram
X axis is the years (age), X axis I want to have 0 20, 25, 30, 35, 40, 45, 50, 55
Y axis is the probability
Someone can help me to get the correct histogram ?
import matplotlib.pyplot as plt
list_age = ['26','28','26','36','38','31','22','31','25','30','37','27','27','29','27','21','27','38','31','41','28','31','28','33','26','39','37','24','31','34','39','33','22', '30','24','29','28','34','27','28','26','26','25','40','24','37','24','28','26','29','26','31','23','31','36','32','25','31','25','33','36','27','28',
'25','27','39','36','30','31','34','23','31','32','31','33','32','39','35','35','22','34','25','35','35','41','20','21','35','32','30','22','21','23','33','25','30','24','39','24','27','22','33','30','27','30','23','29','30','22','31','29','31','24','29','25','24','26','29','31','24','32','21','25','29','30']
list_age.sort()
bins = 55
plt.hist(list_age, bins, facecolor='g')
plt.xlabel('Years')
plt.ylabel('Probability')
plt.grid(True)
plt.show()
You need to convert your list_age to a list of integers first (instead of a list of strings).
Then, simply use the options density (or normed) to display probability and xticks to change the ticks for the x-axis.
import matplotlib.pyplot as plt
list_age = ['26','28','26','36','38','31','22','31','25','30','37','27','27','29','27','21','27','38','31','41','28','31','28','33','26','39','37','24','31','34','39','33','22', '30','24','29','28','34','27','28','26','26','25','40','24','37','24','28','26','29','26','31','23','31','36','32','25','31','25','33','36','27','28',
'25','27','39','36','30','31','34','23','31','32','31','33','32','39','35','35','22','34','25','35','35','41','20','21','35','32','30','22','21','23','33','25','30','24','39','24','27','22','33','30','27','30','23','29','30','22','31','29','31','24','29','25','24','26','29','31','24','32','21','25','29','30']
list_age = [ int(i) for i in list_age ]
bins = len(set(list_age))
plt.hist(list_age, bins = bins, density = True, facecolor = "g") # Replace density by normed if older version of matplotlib
plt.xticks(range(0, 55, 5))
plt.xlabel('Years')
plt.ylabel('Probability')
plt.grid(True)
plt.show()
If you want to display the bars at specific bins, simply define bins at their coordinates:
plt.hist(list_age, bins = [ 0, 20, 25, 30, 35, 40, 45, 50, 55 ], density = True, facecolor = "g")

Piechart with Matplotlib/Python [duplicate]

Alright matplotlib afficionados, we know how to plot a donut chart, but what is better than a donut chart? A double-donut chart. Specifically: We have a set of elements that fall into disjoint categories and sub-categories of the first categorization. The donut chart should have slices for the categories in the outer ring and slices for the sub-categories in the inner ring, obviously aligned with the outer slices.
Is there any library that provides this or do we need to work this out here?
To obtain a double donut chart, you can plot as many pie charts in the same plot as you want. So the outer pie would have a width set to its wedges and the inner pie would have a radius that is less or equal 1-width.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.axis('equal')
width = 0.3
cm = plt.get_cmap("tab20c")
cout = cm(np.arange(3)*4)
pie, _ = ax.pie([120,77,39], radius=1, labels=list("ABC"), colors=cout)
plt.setp( pie, width=width, edgecolor='white')
cin = cm(np.array([1,2,5,6,9,10]))
labels = list(map("".join, zip(list("aabbcc"),map(str, [1,2]*3))))
pie2, _ = ax.pie([60,60,37,40,29,10], radius=1-width, labels=labels,
labeldistance=0.7, colors=cin)
plt.setp( pie2, width=width, edgecolor='white')
plt.show()
Note: I made this code also available in the matplotlib gallery as nested pie example.
I adapted the example you provided; you can tackle your problem by plotting two donuts on the same figure, with a smaller outer radius for one of them.
import matplotlib.pyplot as plt
import numpy as np
def make_pie(sizes, text,colors,labels, radius=1):
col = [[i/255 for i in c] for c in colors]
plt.axis('equal')
width = 0.35
kwargs = dict(colors=col, startangle=180)
outside, _ = plt.pie(sizes, radius=radius, pctdistance=1-width/2,labels=labels,**kwargs)
plt.setp( outside, width=width, edgecolor='white')
kwargs = dict(size=20, fontweight='bold', va='center')
plt.text(0, 0, text, ha='center', **kwargs)
# Group colors
c1 = (226, 33, 7)
c2 = (60, 121, 189)
# Subgroup colors
d1 = (226, 33, 7)
d2 = (60, 121, 189)
d3 = (25, 25, 25)
make_pie([100, 80, 90], "", [d1, d3, d2], ['M', 'N', 'F'], radius=1.2)
make_pie([180, 90], "", [c1, c2], ['M', 'F'], radius=1)
plt.show()

Categories