Plotting Log-normal scale in matplotlib - python

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()

Related

Cartopy fancy box

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()

Color by category in matplotlib using np.where

I'm trying to create a scatter plot with 100 data points and three variables: x value, y value, and category. This information is stored in an ndarray.
I can create the scatter plot, but I don't know how to use a different color for each category. I used the following code for the plot, which seems to work fine (although it's not finished):
def my_plot(data, color_map):
f, ax = plt.subplots()
ax.scatter(data.x, data.y, s = 150, edgecolors = "r")
return f
In my function, color_map is a parameter which refers to a dictionary I created to color the different categories (there are four in total). This is the dictionary:
color_map = {"winter":(15, 28, 75), "spring":(92, 57, 32), "summer":(255, 253, 211), "fall":(174, 12, 12)}
What I would like to do is to somehow integrate this color_map in my function so that each dot in my plot receives a different color.
I think this could be done using np.where to create a mask, but I'm not sure how to proceed...
The color values need to be divided by 255 because matplotlib likes them between 0 and 1.
With this dict you can create an array of colors for the categories:
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import pandas as pd
import numpy as np
color_map = {"winter": (15, 28, 75), "spring": (92, 57, 32), "summer": (255, 253, 211), "fall": (174, 12, 12)}
color_map = {key: (r / 255, g / 255, b / 255,) for key, (r, g, b) in color_map.items()}
N = 200
data = pd.DataFrame({'x': np.random.uniform(1, 9, N), 'y': np.random.uniform(1, 5, N),
'cat': np.random.choice([*color_map.keys()], N)})
fig, ax = plt.subplots()
ax.scatter(data.x, data.y, s=150, color=[color_map[c] for c in data.cat], ec='r')
handles = [Line2D([], [], marker='o', ls='', color=col, markeredgecolor='r', label=label)
for label, col in color_map.items()]
plt.legend(handles=handles, bbox_to_anchor=[1.02, 1.02], loc='upper left')
plt.tight_layout()
plt.show()
PS: A similar plot can be generated with seaborn, which also automatically adds the corresponding legend. Note that the current version of matplotlib (3.3.1) has a problem with the hue parameter. Normally you would add it as hue='cat' but in this version a workaround via .to_list is needed.
import seaborn as sns
ax = sns.scatterplot(x='x', y='y', hue=data['cat'].to_list(), s=150, palette=color_map, edgecolor='r', data=data)

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()

Axis don't show the ticks I want

I want to plot a Ramachandron plot. On this kind of graph, x goes from -180° to 180°, and so does y. I want a tick every 60 degrees. So here is the code I use:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
x = [-179, 179]
y = [-179, 179]
fig = plt.figure(1)
ax = plt.subplot(111)
ax.axis([-180, 180, -180, 180])
ax.set_xticks([-180, -120, -60, 0, 60, 120, 180])
ax.set_yticks([-180, -120, -60, 0, 60, 120, 180])
# 1 bim = 1 degree
# !!! Logarithmic normalization of the colors
plt.hist2d(x, y, bins=180, norm=LogNorm())
plt.colorbar()
plt.show()
On this working example, I'm plotting only two points. But the ticks -180 and 180 are not shown, for none of the axes:
If I change x and y to:
x = [-180, 180]
y = [-180, 180]
I get what I want:
Is there a way to achieve the second result without altering the data ?
You use hist2d, set axis ticks after plotting:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
x = [-179, 179]
y = [-179, 179]
fig = plt.figure(1)
ax = plt.subplot(111)
# 1 bim = 1 degree
# !!! Logarithmic normalization of the colors
plt.hist2d(x, y, bins=180, norm=LogNorm())
plt.colorbar()
ax.axis([-180, 180, -180, 180])
ax.set_xticks([-180, -120, -60, 0, 60, 120, 180])
ax.set_yticks([-180, -120, -60, 0, 60, 120, 180])
plt.show()

Categories