I'm trying to a build a plot that has an exponential function on the top and the utility function on the bottom. With the Y-Axis in the top plot showing the latency and X-Axis as the congestion; similarly, in the second plot, Y-Axis is the throughput and the X-Axis is the congestion.
Where I fail to get is, how do I set the X-Axis as a percentage, and is there a way to superimpose these two graphs.
#!/usr/bin/env python3
import numpy as np
import math
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import matplotlib
fig = plt.figure()
x = np.arange(1,9,1)
y = [math.exp(_) for _ in x]
ax = fig.add_subplot(211)
ax.plot(x, y)
ax.set_ylabel('Y_plot1')
ax.set_xlabel('X_plot1')
ax.set_yticks([],[])
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.yaxis.set_tick_params(which='major', direction='out')
ax.set_ymargin(1)
ax1 = fig.add_subplot(212)
mu = 5
variance = 1
sigma = math.sqrt(variance)
x_normal = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
y_normal = mlab.normpdf(x_normal, mu, sigma)
#y_normal += 1000
x_normal = [0, 0] + list(x_normal)
y_normal = [0, 0] + list(y_normal)
ax1.plot(x_normal, y_normal)
ax1.set_ylabel('Y_plot2')
ax1.set_xlabel('X_plot2')
ax1.set_yticks([],[])
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('left')
ax1.set_ymargin(1)
fig.tight_layout()
fig.savefig('bw-latency' +'.pdf',format='pdf',bbox_inches='tight', pad_inches=0.1, dpi=1000)
plt.clf()
plt.close()
To convert your x-axis to percent, you could normalize x_normaland adjust the xticks:
x_normal = x_normal/(max(x_normal)-min(x_normal)) + min(x_normal)
ax1.plot(x_normal, y_normal)
ax1.set_xticks(np.linspace(0,1,5))
ax1.set_xticklabels([str(int(i*100)) for i in np.linspace(0,1,5)])
To superimpose two graphs, have a look at: https://matplotlib.org/gallery/api/two_scales.html
I your case:
ax3 = ax1.twinx()
y = [math.exp(_) for _ in x_normal]
ax3.plot(x_normal, y,color="r")
EDIT: Is this the kind of output you are seeking?:
Here is the code that worked for me:
def plot_percentage(x, y, ax):
x = x/max(x)
ax.plot(x, y)
ax.set_xticks(np.linspace(0, 1, 10))
ax.set_xticklabels([str(int(i*100)) for i in np.linspace(0,1, 10)])
fig = plt.figure()
x = np.arange(1,9,1)
y = [math.exp(_) for _ in x]
ax = fig.add_subplot(211)
plot_percentage(x, y, ax)
ax.set_ylabel('Y_plot1')
ax.set_xlabel('X_plot1')
ax.set_yticks([],[])
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.yaxis.set_tick_params(which='major', direction='out')
ax.set_ymargin(1)
ax1 = fig.add_subplot(212)
mu = 5
variance = 1
sigma = math.sqrt(variance)
x_normal = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
y_normal = mlab.normpdf(x_normal, mu, sigma)
#y_normal += 1000
x_normal = [0, 0] + list(x_normal)
y_normal = [0, 0] + list(y_normal)
plot_percentage(x_normal, y_normal, ax1)
ax3 = ax1.twinx()
y = [math.exp(_) for _ in x_normal]
plot_percentage(x_normal, y, ax3)
plt.show()
Related
I have this code modified from the topic here:
How to produce a revolution of a 2D plot with matplotlib in Python
The plot contains a subplot in the XY plane and another subplot of the solid of revolution toward the y-axis.
I want to add another subplot that is the solid of revolution toward the x-axis + how to add a legend to each subplot (above them), so there will be 3 subplots.
This is my MWE:
# Compare the plot at xy axis with the solid of revolution
# For function x=(y-2)^(1/3)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
n = 100
fig = plt.figure(figsize=(12,6))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122,projection='3d')
y = np.linspace(np.pi/8, np.pi*40/5, n)
x = (y-2)**(1/3) # x = np.sin(y)
t = np.linspace(0, np.pi*2, n)
xn = np.outer(x, np.cos(t))
yn = np.outer(x, np.sin(t))
zn = np.zeros_like(xn)
for i in range(len(x)):
zn[i:i+1,:] = np.full_like(zn[0,:], y[i])
ax1.plot(x, y)
ax2.plot_surface(xn, yn, zn)
plt.show()
Option 1:
Simply reverse x and y to switch the axes of the function.
x = np.linspace(np.pi/8, np.pi*40/5, n)
y = (x-2)**(1/3)
Option 2:
It is a little complicated. You can also accomplish this by finding the inverse of the original function.
The inverse of f(x) = y = x^3 + 2 is f^{-1}(y) = (y - 2)^(1/3).
I modified the code you provided.
import matplotlib.pyplot as plt
import numpy as np
n = 100
fig = plt.figure(figsize=(14, 7))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222, projection='3d')
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224, projection='3d')
y = np.linspace(np.pi / 8, np.pi * 40 / 5, n)
x = (y - 2) ** (1 / 3)
t = np.linspace(0, np.pi * 2, n)
xn = np.outer(x, np.cos(t))
yn = np.outer(x, np.sin(t))
zn = np.zeros_like(xn)
for i in range(len(x)):
zn[i:i + 1, :] = np.full_like(zn[0, :], y[i])
ax1.plot(x, y)
ax1.set_title("$f(x)$")
ax2.plot_surface(xn, yn, zn)
ax2.set_title("$f(x)$: Revolution around $y$")
# find the inverse of the function
x_inverse = y
y_inverse = np.power(x_inverse - 2, 1 / 3)
xn_inverse = np.outer(x_inverse, np.cos(t))
yn_inverse = np.outer(x_inverse, np.sin(t))
zn_inverse = np.zeros_like(xn_inverse)
for i in range(len(x_inverse)):
zn_inverse[i:i + 1, :] = np.full_like(zn_inverse[0, :], y_inverse[i])
ax3.plot(x_inverse, y_inverse)
ax3.set_title("Inverse of $f(x)$")
ax4.plot_surface(xn_inverse, yn_inverse, zn_inverse)
ax4.set_title("$f(x)$: Revolution around $x$")
plt.tight_layout()
plt.show()
Im not sure if i use the wrong data or if there is and edit i need to do and not seeing it. It would be nice if someone could take a look at the code. The problem here is that yerr at the first bar is at x=0 and in the image the yerr is somewhere around 2.5
Does someone know what i did wrong or forgot to edit?
the end result should be:
my code:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
y_raw = np.random.randn(1000).cumsum() + 15
x_raw = np.linspace(0, 24, y_raw.size)
x_pos = x_raw.reshape(-1, 100).min(axis=1)
y_avg = y_raw.reshape(-1, 100).mean(axis=1)
y_err = y_raw.reshape(-1, 100).ptp(axis=1)
bar_width = x_pos[1] - x_pos[0]
x_pred = np.linspace(0, 30)
y_max_pred = y_avg[0] + y_err[0] + 2.3 * x_pred
y_min_pred = y_avg[0] - y_err[0] + 1.2 * x_pred
barcolor, linecolor, fillcolor = 'wheat', 'salmon', 'lightblue'
fig, axes = fig, ax = plt.subplots()
axes.set_title(label="Future Projection of Attitudes", fontsize=15)
plt.xlabel('Minutes since class began', fontsize=12)
plt.ylabel('Snarkiness (snark units)', fontsize=12)
fig.set_size_inches(8, 6, forward=True)
axes.fill_between(x_pred, y_min_pred, y_max_pred ,color='lightblue')
axes.plot(x_raw, y_raw, color='salmon')
vert_bars = axes.bar(x_pos, y_avg, yerr=y_err, color='wheat', width = bar_width, edgecolor='grey',error_kw=dict(lw=1, capsize=5, capthick=1, ecolor='gray'))
axes.set(xlim=[0, 30], ylim=[0,100])
plt.show()
yerr is meant to be the difference between the mean and the min/max. Now you're using the full difference between max and min. You might divide it by 2 to get a better approximation. To obtain the exact values, you could calculate them explicitly (see code example).
Further, by default, the bars are center aligned vs their x-position. You can use align='edge' to left-align them (as x_pos is calculated as the minimum of the range the bar represents). You could also set clip_on=False in the err_kw to make sure the error bars are never clipped by the axes.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
y_raw = np.random.randn(1000).cumsum() + 15
x_raw = np.linspace(0, 24, y_raw.size)
x_pos = x_raw.reshape(-1, 100).min(axis=1)
y_avg = y_raw.reshape(-1, 100).mean(axis=1)
y_min = y_raw.reshape(-1, 100).min(axis=1)
y_max = y_raw.reshape(-1, 100).max(axis=1)
bar_width = x_pos[1] - x_pos[0]
x_pred = np.linspace(0, 30)
y_max_pred = y_avg[0] + y_err[0] + 2.3 * x_pred
y_min_pred = y_avg[0] - y_err[0] + 1.2 * x_pred
barcolor, linecolor, fillcolor = 'wheat', 'salmon', 'lightblue'
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_title(label="Future Projection of Attitudes", fontsize=15)
ax.set_xlabel('Minutes since class began', fontsize=12)
ax.set_ylabel('Snarkiness (snark units)', fontsize=12)
ax.fill_between(x_pred, y_min_pred, y_max_pred, color='lightblue')
ax.plot(x_raw, y_raw, color='salmon')
vert_bars = ax.bar(x_pos, y_avg, yerr=(y_avg - y_min, y_max - y_avg),
color='wheat', width=bar_width, edgecolor='grey', align='edge',
error_kw=dict(lw=1, capsize=5, capthick=1, ecolor='grey', clip_on=False))
ax.set(xlim=[0, 30], ylim=[0, 100])
plt.tight_layout()
plt.show()
I am trying to show both cumulative and non-cumulative distributions on the same plot.
fig, ax = plt.subplots(figsize=(10, 5))
n, bins, patches = ax.hist(x, n_bins, density=True, stacked=True, histtype='step',
cumulative=True, label='Empirical cumulative')
# Overlay a non-cumulative histogram.
ax.hist(x, bins=bins, density=True, stacked=True, histtype='step', cumulative=False, label='Empirical non-cumulative')
plt.show()
The Empirical cumulative curve looks well and the values do not exceed 1. However, the Empirical non-cumulative curve has Y values higher than 1. How can I normalize them?
Update:
Sample data:
n_bins = 20
x = [
0.0051055006412772065,
0.09770815865459548,
0.20666651037049322,
0.5433266733820051,
0.5717169069724539,
0.5421114013759187,
0.4994941193115986,
0.4391978276380223,
0.3673067648294034,
0.3150259778098451,
0.4072059689437963,
0.5781929593356039,
0.6494934859266276,
0.620882081680377,
0.5845829440637116,
0.515705471234385]
Please see the orange curve.
The easiest way to create a histogram with probability instead of probability density is to use seaborn's sns.histplot(.... stat='probability').
To mimic this with standard matplotlib, you could calculate all values manually. For example:
import matplotlib.pyplot as plt
import numpy as np
n_bins = 20
x = np.random.normal(0, 1, (1000, 3))
bin_edges = np.linspace(x.min(), x.max(), n_bins + 1)
bin_values = np.array([np.histogram(x[:, i], bins=bin_edges)[0] for i in range(x.shape[1])])
cum_values = bin_values.cumsum(axis=1).cumsum(axis=0)
cum_values = cum_values / cum_values.max()
fig, ax = plt.subplots(figsize=(10, 5))
prev = 0
for c in cum_values:
plt.step(np.append(bin_edges, bin_edges[-1]), np.concatenate([[0], c, [prev]]))
prev = c[-1]
ax.set_prop_cycle(None)
prev = 0
for c in cum_values:
c = np.diff(c)
plt.step(np.append(bin_edges, bin_edges[-1]), np.concatenate([[0], c, [c[-1], prev]]), ls='--')
prev = c[-1]
plt.show()
If you have just one distribution, stacked=True doesn't make a difference. The code would be simpler:
import matplotlib.pyplot as plt
import numpy as np
n_bins = 20
x = np.random.normal(0, 1, 1000)
bin_edges = np.linspace(x.min(), x.max(), n_bins + 1)
bin_values = np.histogram(x, bins=bin_edges)[0]
cum_values = bin_values.cumsum()
cum_values = cum_values / cum_values.max()
fig, ax = plt.subplots(figsize=(10, 5))
plt.step(np.append(bin_edges, bin_edges[-1]), np.concatenate([[0], cum_values, [0]]))
ax.set_prop_cycle(None)
c = np.diff(cum_values)
plt.step(np.append(bin_edges, bin_edges[-1]), np.concatenate([[0], c, [c[-1], 0]]), ls='--')
plt.show()
I am trying to combine two colourmap legends in one. Colour values are defined from third (z) data.
I am trying plot one legend colormap with two color scheme.
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.read_excel('C:\\Users\user1\\PycharmProjects\\untitled\\Python_test.xlsx')
x = df['Vp_dry']
y = df['Vs_dry']
q = df['Vp_wet']
w = df['Vs_wet']
fig, ax = plt.subplots()
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, x, y)
x_linspace = np.linspace(min(x - 100), max(x + 100), 100)
power_y = popt[0]*x_linspace ** -popt[1]
ax1 = plt.scatter(x, y, c=df['Porosity'], cmap=plt.cm.Greys, vmin=2, vmax=df['Porosity'].max(), edgecolors="#B6BBBD")
plt.plot(x_linspace, power_y, color='grey', label='Dry')
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, q, w)
q_linspace = np.linspace(min(q - 100), max(q + 100), 100)
power_w = popt[0]*q_linspace ** -popt[1]
ax2 = plt.scatter(q, w, c=df['Porosity'], cmap=plt.cm.Blues, vmin=2, vmax=df['Porosity'].max(), edgecolors="#3D83C1")
plt.plot(q_linspace, power_w, label='Wet')
cbar = fig.colorbar(ax2)
cbar = fig.colorbar(ax1)
cbar.set_label("Porosity (%)")
plt.xlabel('Vp (m/s)')
plt.ylabel('Vs (m/s)')
plt.grid()
plt.legend()
plt.show()
Desired result:
You seem to need a colorbar with two color maps combined, one of them reversed, and have the ticks changed to percentage values.
An approach is to manually create a second subplot, use two images and make it look like a colorbar:
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import numpy as np
# first create some dummy data to plot
N = 100
x = np.random.uniform(0, 10, N)
y = np.random.normal(15, 2, N)
q = np.random.uniform(0, 10, N)
w = np.random.normal(10, 2, N)
df_porosity = np.random.uniform(0, 5, N)
fig, (ax, ax2) = plt.subplots(ncols=2, figsize=(6, 4), gridspec_kw={"width_ratios": [1, 0.08]})
plot1 = ax.scatter(x, y, c=df_porosity, cmap=plt.cm.Greys, vmin=2, vmax=df_porosity.max(), edgecolors="#B6BBBD")
plot2 = ax.scatter(q, w, c=df_porosity, cmap=plt.cm.Blues, vmin=2, vmax=df_porosity.max(), edgecolors="#3D83C1")
img_cbar = np.linspace(0, 1, 256).reshape(256, 1)
ax2.imshow(img_cbar, cmap=plt.cm.Blues, extent=[0, 1, 1, 0]) # aspect='auto')
ax2.imshow(img_cbar, cmap=plt.cm.Greys, extent=[0, 1, -1, 0])
ax2.set_ylim(-1, 1)
ax2.set_aspect(10)
ax2.set_ylabel("Porosity (%)")
ax2.yaxis.set_label_position("right")
ax2.set_xticks([])
ax2.yaxis.tick_right()
# optionally show the ticks as percentage, where 1.0 corresponds to 100 %
ax2.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
plt.tight_layout()
plt.show()
I have a set of 100 random 2D points (between 0 and 20) in a scatter plot with 2 sub plots surrounding the main. When I zoom in the main scatter plot, the range on the subplots gets shrunk, however I can see points from outside the zoom window region.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import random
numPoints = 100
x = [random.uniform(0, 20) for i in range(numPoints)]
y = [random.uniform(0, 20) for i in range(numPoints)]
# Set up the axes with gridspec
fig = plt.figure(figsize=(6, 6), constrained_layout=True)
grid = fig.add_gridspec(ncols=2, nrows=2, width_ratios=[0.3, 5], height_ratios=[5, 0.3])
main_ax = fig.add_subplot(grid[:-1, 1:])
main_ax.plot(x, y, 'ok', markersize=3, alpha=0.2)
y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)
x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)
x_hist.plot(
x, [0 for i in x],
'ok',
color='gray'
)
x_hist.invert_yaxis()
y_hist.plot(
[0 for i in y], y,
'ok',
color='gray'
)
y_hist.invert_xaxis()
main_ax.grid(True, lw = 1, ls = '--', c = '.75')
x_hist.grid(True, axis="x", lw = 1, ls = '--', c = '.75')
y_hist.grid(True, axis="y", lw = 1, ls = '--', c = '.75')
plt.show()
I am trying to get the dots in the left and bottom sub plots of the above image to match just what you see in the main plot (3 points).
Instead they show everything in that direction. The Left subplot shows every point on the x axis between 0 and 2.5. The bottom subplot shows every point on the y axis between 10 and 12.5.
You would need to filter the data, depending on the limits of the main axes. One can connect callbacks on zoom events, see Matplotlib: Finding out xlim and ylim after zoom and connect them to a function that performs the filtering on the data.
import numpy as np
import matplotlib.pyplot as plt
numPoints = 100
x = np.random.rand(numPoints)*20
y = np.random.rand(numPoints)*20
zeros = np.zeros_like(x)
# Set up the axes with gridspec
fig = plt.figure(figsize=(6, 6), constrained_layout=True)
grid = fig.add_gridspec(ncols=2, nrows=2, width_ratios=[0.3, 5], height_ratios=[5, 0.3])
ax_main = fig.add_subplot(grid[:-1, 1:])
ax_y = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=ax_main)
ax_x = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=ax_main)
ax_main.plot(x, y, 'ok', markersize=3, alpha=0.2)
xline, = ax_x.plot(x, zeros, marker='o', ls="none", color='gray')
yline, = ax_y.plot(zeros, y, marker='o', ls="none", color='gray')
ax_main.grid(True, lw = 1, ls = '--', c = '.75')
ax_y.grid(True, axis="x", lw = 1, ls = '--', c = '.75')
ax_x.grid(True, axis="y", lw = 1, ls = '--', c = '.75')
def xchange(evt):
ymin, ymax = ax_main.get_ylim()
filt = (y <= ymax) & (y >= ymin)
xline.set_data(x[filt], zeros[filt])
def ychange(evt):
xmin, xmax = ax_main.get_xlim()
filt = (x <= xmax) & (x >= xmin)
yline.set_data(zeros[filt], y[filt])
ax_main.callbacks.connect('xlim_changed', ychange)
ax_main.callbacks.connect('ylim_changed', xchange)
plt.show()