Related
I want to generate a plot like the below:
At the moment I am trying to play around with the alpha parameter:
import numpy as np
from matplotlib import pyplot as plt
xlocations_edensity = np.loadtxt("edensity_xaxis.txt")
ylocations_edensity = np.loadtxt("edensity_yaxis.txt")
xlocsedensity, ylocsedensity = np.meshgrid(xlocations_edensity, ylocations_edensity)
xlocations_Efield = np.loadtxt("Efield_x_axis.txt")
ylocations_Efield = np.loadtxt("Efield_y_axis.txt")
xlocsEfield, ylocsEfield = np.meshgrid(xlocations_Efield, ylocations_Efield)
edensitytensor = np.load("edensitytensor.npy") # shape (76, 257, 65)
Efieldtensor = np.load("Efieldtensor.npy")
fig, ax = plt.subplots()
ax.set(xlabel="x position [um]", ylabel="y position [um] \n")
pos2 = ax.pcolor(xlocations_Efield, ylocations_Efield, Efieldtensor[40, :, :].T, cmap="Reds", alpha=0.9)
fig.colorbar(pos2, ax=ax, label="\n Efield value [MV/m]")
pos1 = ax.pcolor(xlocations_edensity, ylocations_edensity, edensitytensor[100, :, :].T, cmap="Blues", alpha=0.5)
fig.colorbar(pos1, ax=ax, label="\n electron density value [cm^(-3)]")
plt.savefig("Efield_edensity_map.pdf")
But changing the order of plotting, I get different results. One color map ''hides'' the other.
Say I plot the Reds one first, it appears and the Blues one is hidden.
The other way around, Blues first and Reds first, the Blues hides the Reds.
The result of the above code is:
Do you have anything in mind what shall I do?
Thank you!
Setting the alpha value of the pcolor call is not that good because it applies the same transparency to all the colors on the colormap.
You could use a custom colormap with an evolving transparency, I present my try with linear and sigmoidal evolutions of alpha, you could try others. I created dummy noisy data with a Gaussian pulse to simulate the data as in your example.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
# generating dummy data
nx, ny = 257, 65
x_field, y_field = np.linspace(0,10,nx), np.linspace(0,6,ny)
field = np.random.rand(nx,ny)
# normalizing
field -= np.min(field); field /= np.max(field)
x_density, y_density = np.linspace(1,6,nx), np.linspace(1,6,ny)
Y, X = np.meshgrid(y_density,x_density,)
density = np.random.rand(nx,ny) # shape (76, 257, 65)
gaussian_center = (4.0,4.0)
distance_square = (X - gaussian_center[0])**2 + (Y - gaussian_center[1])**2
density += 5.0*np.exp(-distance_square/4.0)
# normalizing
density -= np.min(density); density /= np.max(density)
# getting the original colormap
orig_cmap = plt.get_cmap('Blues')
cmap_n = orig_cmap.N
derived_cmap = orig_cmap(np.arange(cmap_n))
fig, axs = plt.subplots(
4,3,
gridspec_kw={"width_ratios":[1, 0.025, 0.025]},
figsize=(10,8),
constrained_layout=True)
# original
row_subplot = 0
ax = axs[row_subplot,0]
ax.set_ylabel("original")
image_field = ax.pcolor(
x_field, y_field, field.T,
cmap="Reds", shading='auto')
fig.colorbar(image_field, cax=axs[row_subplot,-2],)
image_density = ax.pcolor(
x_density, y_density, density.T,
cmap=orig_cmap, shading="auto")
fig.colorbar(image_density, cax=axs[row_subplot,-1],)
# option 1 - transparent pseudocolor for the above image
row_subplot = 1
ax = axs[row_subplot,0]
ax.set_ylabel("transparent pcolor")
image_field = ax.pcolor(
x_field, y_field, field.T,
alpha=1.0, cmap="Reds", shading='auto')
fig.colorbar(image_field, cax=axs[row_subplot,-2],)
image_density = ax.pcolor(
x_density, y_density, density.T,
alpha=0.5, cmap=orig_cmap, shading="auto")
fig.colorbar(image_density, cax=axs[row_subplot,-1],)
# option 2 - linear gradient colormap
linear_cmap = derived_cmap.copy()
linear_cmap[:,-1] = np.arange(cmap_n)/cmap_n
linear_cmap = ListedColormap(linear_cmap)
row_subplot = 2
ax = axs[row_subplot,0]
ax.set_ylabel("linear gradient")
image_field = ax.pcolor(
x_field, y_field, field.T,
cmap="Reds", shading='auto')
fig.colorbar(image_field, cax=axs[row_subplot,-2],)
image_density = ax.pcolor(
x_density, y_density, density.T,
cmap=linear_cmap, shading="auto")
fig.colorbar(image_density, cax=axs[row_subplot,-1],)
# option 3 - sigmoid gradient
sigmoid_cmap = derived_cmap.copy()
x = np.linspace(-10,10,cmap_n)
sigmoid_cmap[:,-1] = np.exp(x)/(np.exp(x) + 1)
sigmoid_cmap = ListedColormap(sigmoid_cmap)
row_subplot = 3
ax = axs[row_subplot,0]
ax.set_ylabel("sigmoid gradient")
image_field = ax.pcolor(
x_field, y_field, field.T,
cmap="Reds", shading='auto')
fig.colorbar(image_field, cax=axs[row_subplot,-2],)
image_density = ax.pcolor(
x_density, y_density, density.T,
cmap=sigmoid_cmap, shading="auto")
fig.colorbar(image_density, cax=axs[row_subplot,-1],)
Similar questions to this have been asked before but not using these exact two plotting functions together so here we are:
I have a column from a pandas DataFrame that I am plotting both a histogram and the KDE. However, when I plot them, the y-axis is using the raw data value range instead of discrete number of samples/bin (what I want). How can I fix this? The actual plot is perfect, but the y-axis is wrong.
Data:
t2 = [140547476703.0, 113395471484.0, 158360225172.0, 105497674121.0, 186457736557.0, 153705359063.0, 36826568371.0, 200653068740.0, 190761317478.0, 126529980843.0, 98776029557.0, 132773701862.0, 14780432449.0, 167507656251.0, 121353262386.0, 136377019007.0, 134190768743.0, 218619462126.0, 07912778721.0, 215628911255.0, 147024833865.0, 94136343562.0, 135685803096.0, 165901502129.0, 45476074790.0, 125195690010.0, 113910844263.0, 123134290987.0, 112028565305.0, 93448218430.0, 07341012378.0, 93146854494.0, 132958913610.0, 102326700019.0, 196826471714.0, 122045354980.0, 76591131961.0, 134694468251.0, 120212625727.0, 108456858852.0, 106363042112.0, 193367024628.0, 39578667378.0, 178075400604.0, 155513974664.0, 132834624567.0, 137336282646.0, 125379267464.0]
Code:
fig = plt.figure()
# plot hist + kde
t2[t2.columns[0]].plot.kde(color = "maroon", label = "_nolegend_")
t2[t2.columns[0]].plot.hist(density = True, edgecolor = "grey", color = "tomato", title = t2.columns[0])
# plot mean/stdev
m = t2[t2.columns[0]].mean()
stdev = t2[t2.columns[0]].std()
plt.axvline(m, color = "black", ymax = 0.05, label = "mean")
plt.axvline(m-2*stdev, color = "black", ymax = 0.05, linestyle = ":", label = "+/- 2*Stdev")
plt.axvline(m+2*stdev, color = "black", ymax = 0.05, linestyle = ":")
plt.legend()
What it looks like now:
If you want the real counts, the you'll need to scale the KDE up by the width of the bins multiplied by the number of observations. The trickiest part is accessing the data pandas uses to plot the KDE. (I've removed parts related to the legend to simplify the problem at hand).
import matplotlib.pyplot as plt
import numpy as np
# Calculate KDE, get data
axis = t2[t2.columns[0]].plot.kde(color = "maroon", label = "_nolegend_")
xdata = axis.get_children()[0]._x
ydata = axis.get_children()[0]._y
plt.clf()
# Real figure
fig, ax = plt.subplots(figsize=(7,5))
# Plot Histogram, no density.
x = ax.hist(t2[t2.columns[0]], edgecolor = "grey", color = "tomato")
# size of the bins * N obs
scale = np.diff(x[1])[0]*len(t2)
# Plot scaled KDE
ax.plot(xdata, ydata*scale, color='blue')
ax.set_ylabel('N observations')
plt.show()
I'm trying to create plot with shadings which are based on this MIC(1) line.
Different shading above than beneath.
from scipy import stats
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def createSkewDist(mean, sd, skew, size):
# calculate the degrees of freedom 1 required to obtain the specific skewness statistic, derived from simulations
loglog_slope=-2.211897875506251
loglog_intercept=1.002555437670879
df2=500
df1 = 10**(loglog_slope*np.log10(abs(skew)) + loglog_intercept)
# sample from F distribution
fsample = np.sort(stats.f(df1, df2).rvs(size=size))
# adjust the variance by scaling the distance from each point to the distribution mean by a constant, derived from simulations
k1_slope = 0.5670830069364579
k1_intercept = -0.09239985798819927
k2_slope = 0.5823114978219056
k2_intercept = -0.11748300123471256
scaling_slope = abs(skew)*k1_slope + k1_intercept
scaling_intercept = abs(skew)*k2_slope + k2_intercept
scale_factor = (sd - scaling_intercept)/scaling_slope
new_dist = (fsample - np.mean(fsample))*scale_factor + fsample
# flip the distribution if specified skew is negative
if skew < 0:
new_dist = np.mean(new_dist) - new_dist
# adjust the distribution mean to the specified value
final_dist = new_dist + (mean - np.mean(new_dist))
return final_dist
desired_mean = 30
desired_skew = 1.5
desired_sd = 20
final_dist = createSkewDist(mean=desired_mean, sd=desired_sd, skew=desired_skew, size=1000000)
# inspect the plots & moments, try random sample
fig, ax = plt.subplots(figsize=(12,7))
sns.distplot(final_dist,
hist=False,
ax=ax,
color='darkred',
kde_kws=dict(linewidth=4))
l1 = ax.lines[0]
# Get the xy data from the lines so that we can shade
x1 = l1.get_xydata()[:,0]
x1[0] = 0
y1 = l1.get_xydata()[:,1]
y1[0] = 0
ax.fill_between(x1,y1, color="lemonchiffon", alpha=0.3)
ax.set_ylim(0.0001,0.03)
ax.axhline(0.002, ls="--")
ax.set_xlim(1.5, 200)
ax.set_yticklabels([])
ax.set_xticklabels([])
trans = transforms.blended_transform_factory(
ax.get_yticklabels()[0].get_transform(), ax.transData)
ax.text(0,0.0025, "{}".format("MIC(1) = 1"), color="blue", transform=trans,
ha="right", va="top", fontsize = 12)
trans_2 = transforms.blended_transform_factory(
ax.get_xticklabels()[0].get_transform(), ax.transData)
ax.text(84,0, "{}".format("\n84"), color="darkred", transform=trans_2,
ha="center", va="top", fontsize = 12)
ax.text(1.5,0, "{}".format("\n0"), color="darkred", transform=trans_2,
ha="center", va="top", fontsize = 12)
ax.axvline(x = 84, ymin = 0, ymax = 0.03, ls = '--', color = 'darkred' )
ax.set_yticks([])
ax.set_xticks([])
ax.spines['top'].set_color(None)
ax.spines['right'].set_color(None)
ax.spines['left'].set_linewidth(2)
ax.spines['bottom'].set_linewidth(2)
ax.set_ylabel("Concentration [mg/L]", labelpad = 80, fontsize = 15)
ax.set_xlabel("Time [h]", labelpad = 80, fontsize = 15)
ax.set_title("AUC/MIC", fontsize = 20, pad = 30)
plt.annotate("AUC/MIC",
xy=(18, 0.02),
xytext=(18, 0.03),
arrowprops=dict(arrowstyle="->"), fontsize = 12);
;
That's what I have:
And that's what I'd like to have (it's done in paint, so forgive me :) ):
I was experimenting with fill_between and fill_betweenx. However, without any satisfying results. Definitely, run out of ideas. I'd really appreciate any help on this. Best wishes!
Your fill_between works as expected. The problem is that color="lemonchiffon" with alpha=0.3 is barely visible. Try to use a brighter color and/or a higher value for alpha.
So, this colors the part of the graph between zero and the kde curve.
Now, to create a different coloring above and below the horizontal line, where= and np.minimum can be used in fill_between:
pos_hline = 0.002
ax.fill_between(x1, pos_hline, y1, color="yellow", alpha=0.3, where=y1 > pos_hline)
ax.fill_between(x1, 0, np.minimum(y1, pos_hline), color="blue", alpha=0.3)
Without where=y1 > pos_hline, fill_between would also color the region above the curve where the curve falls below that horizontal line.
PS: Note that sns.histplot has been deprecated since Seaborn version 0.11. To only plot the kde curve, you can use sns.kdeplot:
sns.kdeplot(final_dist, ax=ax, color='darkred', linewidth=4)
I've written the following program using python in order to graph multiple sine waves of different frequencies, as well as display the points of intersection between them;
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
fig = plt.figure()
ax = plt.axes()
f1 = float(input("Enter first frequency: "))
f2 = float(input("Enter second frequency: "))
t = np.linspace(0, 10, 1000)
y1 = np.sin(2*np.pi*f1*t)
y2 = np.sin(2*np.pi*f2*t)
plt.plot(t,y1, color = "firebrick", label = "sin({}Hz)".format(f1))
plt.plot(t,y2, color = "teal", label = "sin({}Hz)".format(f2))
plt.axhline(y = 0, color = "grey", linestyle = "dashed", label = "y = 0")
idx = np.argwhere(np.diff(np.sign(y1 - y2))).flatten()
plt.plot(t[idx], y1[idx], 'k.')
plt.legend(loc = "best", frameon=True, fancybox = True,
shadow = True, facecolor = "white")
plt.axis([-0.5, 10.5, -1.5, 1.5])
plt.title("Sine Waves")
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.show()
Sometimes the output looks just as it's supposed to, for example
in this screenshot.
However, at other times i obtain an undesired output such as in this one.
Could someone demonstrate how to fix this problem? Thank you.
I would like to suggest you to increase your time discretization or simply plot these waves in terms of number of n_T periods of the highest/lowest frequency to avoid undersampling problems. For instance, if you more interested in the lowest frequency you could modified your code as follows:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
fig = plt.figure()
ax = plt.axes()
f1 = float(input("Enter first frequency: "))
f2 = float(input("Enter second frequency: "))
n_T = float(input("Enter number of periods of lowest frequency to display: "))
t_max = n_T/min(f1,f2) # change here max or min if you want highest or lowest frequency to be represented on n_T periods
t = np.linspace(0, t_max, 1000)
y1 = np.sin(2*np.pi*f1*t)
y2 = np.sin(2*np.pi*f2*t)
plt.plot(t,y1, color = "firebrick", label = "sin({}Hz)".format(f1))
plt.plot(t,y2, color = "teal", label = "sin({}Hz)".format(f2))
plt.axhline(y = 0, color = "grey", linestyle = "dashed", label = "y = 0")
idx = np.argwhere(np.diff(np.sign(y1 - y2))).flatten()
plt.plot(t[idx], y1[idx], 'k.')
plt.legend(loc = "best", frameon=True, fancybox = True,
shadow = True, facecolor = "white")
plt.axis([-0.05*t_max, 1.05*t_max, -1.5, 1.5])
plt.title("Sine Waves")
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.show()
which gives for n_T=3 and f1=200 and f2=400 Hz :
and for your problematic case f1=520 and f2=750 Hz:
BONUS : if you want to compute automatically the minimum number n_T of periods to display the exact number of unique intersections between the two oscillating components. First, convert user inputs f1 and f2 from floats to integers, then find the lowest common multiple lcm between them (using greatest common divisor gcd function from math) and divide it by the highest frequency, here you are:
from math import gcd
def lcm(a,b):
"""
Compute the lowest common multiple of a and b
"""
return a*b/gcd(a,b)
# minimum of n_T periods to visualize every unique intersections of waves
n_T = lcm(f1,f2)/max(f1,f2)
for instance for f1=250 and f2=300 Hz, n_T=1500/300=5 which will give:
I am able to make a matplotlib histogram no problem. However, I'm wondering if it's possible to use something like fillbetween to change the fill color of the central 95% CI of my data.
I can only get fillbetween to work when if I use a trick with a numpy histogram and bincenters. i.e.:
bins = np.linspace(-a.max(),a.max(),400)
hist = np.histogram(a,bins = bins)[0]
bincenters = 0.5*(bins[1:] + bins[:-1])
b = plt.plot(bincenters,hist, linestyle = 'None')
plt.fill_between(bincenters,hist, color = '#7f7f7f')
plt.fill_between(bincenters, hist, interpolate=False,
where=((bincenters>=lower_p) & (bincenters<=upper_p)), hatch = '...', facecolor = '#7f7f7f')```
Here's my existing code that I'd rather use to create the matplotlib histogram (which I think looks better) with some extras plotting on top:
#Create Histogram
axes[1] = boota.plot.hist(ax = axes[1],bins = 50, legend = None, histtype = 'bar', color = '#7f7f7f')
axes[1].set_xlabel('Spatial Decay Rate (α)', size = 16, fontweight = 'bold')
axes[1].set_ylabel('Frequency', labelpad = 11, size = 16, fontweight = 'bold')
#Ticklabels
axes[0].tick_params(labelsize = 14)
axes[1].tick_params(labelsize = 14)
#draw vertical line at remote powerlaw (rem_a)
rem_a = 0.649
axes[1].axvline(x=rem_a, color='k', linestyle='dashed', linewidth=1.5, label='remote decay \nrate $α_r$ = 0.649')
legend = axes[1].legend(ncol = 1, loc = 'upper left', fontsize='large')
legend.draw_frame(False)
at2 = AnchoredText("B",prop=dict(size=20), loc='upper right',frameon=False)
axes[1].add_artist(at2)
Check out fill_betweenx which I think is better fit here
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
arr = np.random.normal(size=500)
ci = norm(*norm.fit(arr)).interval(0.95) # fit a normal distribution and get 95% c.i.
height, bins, patches = plt.hist(arr, alpha=0.3)
plt.fill_betweenx([0, height.max()], ci[0], ci[1], color='g', alpha=0.1) # Mark between 0 and the highest bar in the histogram