Python manipulate axis (x and y) update - python

we measure the radius over an entire device (each degree, 360 points), which is around 148mm. It should be between 146 and 150.
If you plot the data with the corresponding limits, you get this:
CirclPlot
I like to change the axis that between -145 and 145 is small, and between 145- 150 / -145 - -150 is large. So I can see the measured value nice in between the limits.
Is that possible with python?
import matplotlib.pyplot as plt
import matplotlib.scale as mscale
import pandas as pd
#read CSV
EBRData = pd.read_csv('C://Users/vanderey/Documents/MATLAB/EBRTest2.csv', header = 0)
# Define data
Dates = EBRData['Date']
Rx = EBRData['xCoat']
Ry = EBRData['yCoat']
RLSLx = EBRData['xCoat_LSL']
RLSLy = EBRData['yCoat_LSL']
RUSLx = EBRData['xCoat_USL']
RUSLy = EBRData['yCoat_USL']
#Create plot
my_dpi=96
plt.figure(figsize=(480/my_dpi, 480/my_dpi), dpi=my_dpi)
plt.plot(Rx, Ry, color='blue', marker='.', linewidth=1, alpha=0.4)
plt.plot(RLSLx, RLSLy, color='red', marker='.', linewidth=1, alpha=0.4)
plt.plot(RUSLx, RUSLy, color='red', marker='.', linewidth=1, alpha=0.4)
plt.title('EBR')
plt.show()

If radius is what you want to show, I'd also recommend to calculate R from x and y measurements and put that into a plot together with the target limits.
You can do so by calculating the complete polar coordinates from your x/y-values
phi = np.arctan2(df.yCoat, df.xCoat)
R = pd.DataFrame(np.sqrt(df.xCoat.values**2 + df.yCoat.values**2), columns=['R'], index=phi)
If you rather like to plot over the nominal angular values instead of the actual measured angle positions, you could set phi also to e.g.
phi = np.linspace(-np.pi, np.pi, 360, endpoint=False)
However, this can be plotted simply as a normal line plot with two indicated limit lines like
R.plot()
plt.hlines(146, -np.pi, np.pi, 'k')
plt.hlines(150, -np.pi, np.pi, 'k')
or e.g. as a polar plot
f = plt.figure()
ax = f.add_subplot(111, projection='polar')
ax.set_rlim(144, 152)
plt.plot(R, 'b.-')
ax.fill_between(np.linspace(-np.pi, np.pi, 360), 140, 146, color='gray')
ax.fill_between(np.linspace(-np.pi, np.pi, 360), 150, 160, color='gray')
To show samples outside the wanted range, you can simply add e.g.
plt.plot(R[R<146], 'r.')
plt.plot(R[R>150], 'r.')
to immediately see if there's a problem:

Related

Draw the profile of a scatter plot

I have a scatter plot of Lc and Fc values (please, refer to plot1).
Lc= [360.66832393 388.26294316 392.9410819 ... 384.31751584 403.52581547
384.22929343]
Fc= [77.3294787 47.5926941 44.53032575 ... 50.44012265 38.99666318
50.54763385]
plot.scatter(Lc, Fc)
I would like to draw the Fc profile of this scatter plot as shown in plot2. Does anyone have an efficient way to do it?
Here is an idea drawing a Gaussian curve through each of the points and then take the maximum of these curves. You might want to experiment with the curve widths.
import matplotlib.pyplot as plt
import numpy as np
low_lim = 30
fc = np.random.rand(120) * np.random.rand(120) * 120
fc = fc[fc > low_lim]
lc = np.random.uniform(50, 250, len(fc))
x = np.linspace(0, 300, 5000)
sigma = 15
ys = np.exp(- np.power((x.reshape(-1, 1) - lc) / sigma, 2) / 2) * fc
ymax = ys.max(axis=1)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(15, 4))
for ax in (ax1, ax2):
if ax == ax1:
ax.plot(x, ymax, color='black', ls=':', lw=3)
for l, f, y in zip(lc, fc, ys.T):
ax.plot(x, y)
ax.fill_between(x, 0, y, color='r', alpha=0.05)
else:
ax.plot(x, ymax, color='black', lw=2)
ax.fill_between(x, 0, ymax, color='r', alpha=0.2)
ax.scatter(lc, fc, color='darkorange')
ax.axhline(low_lim, ls='--', color='skyblue')
ax.set_ylim(ymin=0)
ax.margins(x=0)
plt.tight_layout()
plt.show()
Here is an attempt to smooth out the sharp corners, which might or might not work with your data. The effect is only very local; trying to smooth out more resulted in also losing the general shape.
from scipy.special import softmax
ys = np.exp(- np.power((x.reshape(-1, 1) - lc) / sigma, 2) / 2) * fc
softmax_weights = softmax(np.power(ys, 0.8), axis=1)
ymax = np.sum(ys * softmax_weights, axis=1)

How to get rid of discontinuity on windrose pcolormap plot python

I am attempting to plot a windrose with binned concentration values. Following the advice from this post, and some modifications, I have created a plot. However, there is a discontinuity at around 0 deg. Any help would be greatly appreciated!
Here is my code:
wd = list(merge_all_apr['Wind Dir (10s deg)'])
conc = list(merge_all_apr['Mean_CO2'])
ws = list(merge_all_apr['Wind Spd (km/h)'])
wd_rad = np.radians(np.array(wd))
conc = np.array(conc, dtype=np.float)
wind_speed = np.linspace(min(ws), max(ws), 16)
WD, WS = np.meshgrid(np.linspace(0, 2*np.pi, 36), wind_speed)
print (WS)
Z = interpolate.griddata((wd_rad, ws), oz, (WD, WS), method='linear')
fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
cmap = plt.get_cmap('jet')
#cmap.set_under('none')
img = ax.pcolormesh(WD, WS, Z, cmap=cmap, vmin=0, vmax=40, alpha = 0.70)
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
plt.colorbar(img)
plt.show()
And the result is:
As a scatter, it works fine and looks like
I'm not sure how to provide the data in a concise way, but any help would be greatly appreciated!!
You could also provide values for wd_rad just lower than 0 and larger than 2*pi using np.where, adding 2*pi for small values and subtracting 2*pi for large values. np.tile(ws, 2) and np.tile(conc, 2) then associate the extended version of wd_rad with the same concentration values. Using also these extended values in interpolate.griddata makes sure the concentration values wrap around at 0 and 2*pi.
As an aside, note that 'jet' is a colormap that looks nice, but is very misleading as it creates yellow highlights at the wrong spots. (Also, converting pandas columns to lists is quite slow and memory consuming, better leave them in their numpy array format.)
The code below supposes that oz in the question is the same array as conc.
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# wd = merge_all_apr['Wind Dir (10s deg)']
# conc = merge_all_apr['Mean_CO2']
# ws = merge_all_apr['Wind Spd (km/h)']
N = 100
wd = np.random.uniform(0, 360, N)
conc = np.random.uniform(0, 40, N)
ws = np.random.uniform(0, 45, N)
wd_rad = np.radians(np.array(wd))
conc = np.array(conc, dtype=np.float)
wd_rad_ext = np.where(wd_rad < np.pi, wd_rad + 2 * np.pi, wd_rad - 2 * np.pi)
wind_speed = np.linspace(min(ws), max(ws), 16)
WD, WS = np.meshgrid(np.linspace(0, 2 * np.pi, 37), wind_speed)
Z = interpolate.griddata((np.hstack([wd_rad, wd_rad_ext]), np.tile(ws, 2)), np.tile(conc, 2),
(WD, WS), method='linear', rescale=True)
fig, axes = plt.subplots(ncols=2, figsize=(10, 4), subplot_kw={"projection": "polar"})
cmap = plt.get_cmap('magma')
for ax in axes:
if ax == axes[0]:
img = ax.pcolormesh(WD, WS, Z, cmap=cmap, vmin=0, vmax=40)
else:
img = ax.scatter(wd_rad, ws, c=conc, cmap=cmap, vmin=0, vmax=40)
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
plt.colorbar(img, ax=ax, pad=0.12)
plt.show()
If you don't want an interpolation, but want to draw segments of 10 degrees wide to represent each area, plt.hist2d can be employed.
Special parameters to hist2d:
bins=(np.linspace(0, 2 * np.pi, 37), np.linspace(min(ws), max(ws), 17)): the wind directions will be divided into 36 regions (37 boundaries); the speeds will be divided into 16 regions
weights=conc: instead of a usual histogram that counts the number of values into each little region, use the concentrations; when multiple concentrations are measured in the same little region, they are averaged
cmin=0.001: leave regions blank when their concentration value is less than 0.001
cmap = 'magma_r': use the reversed 'magma' colormap, so the high values get dark and the low get light colors (see the docs about other colormaps that might be more suitable to illustrate the data, but try not to use 'jet')
The return values of hist2d are a matrix of histogram values, the bin boundaries (x and y) and a collection of colored patches (which can be used as input for the colormap).
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
N = 100
wd = np.random.uniform(0, 360, N)
conc = np.random.uniform(0, 40, N)
ws = np.random.uniform(0, 45, N)
wd_rad = np.radians(np.array(wd))
conc = np.array(conc, dtype=np.float)
fig, axes = plt.subplots(ncols=2, figsize=(10, 4), subplot_kw={"projection": "polar"})
cmap = 'magma_r'
for ax in axes:
if ax == axes[0]:
_, _, _, img = ax.hist2d(wd_rad, ws, bins=(np.linspace(0, 2 * np.pi, 37), np.linspace(min(ws), max(ws), 17)),
weights=conc, cmin=0.001, cmap=cmap, vmin=0, vmax=40)
else:
img = ax.scatter(wd_rad, ws, c=conc, cmap=cmap, vmin=0, vmax=40)
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
plt.colorbar(img, ax=ax, pad=0.12)
plt.show()

Custom Spider chart --> Display curves instead of lines between point on a polar plot in matplotlib

I have measured the positions of different products in different angles positions (6 values in steps of 60 deg. over a complete rotation). Instead of representing my values on a Cartesian graph where 0 and 360 are the same point, I want to use a polar graph.
With matplotlib, I got a spider chart type graph, but I want to avoid straight lines between points and display and extrapolated values between those. I have a solution that is kind of OK, but I was hoping there is a nice "one liner" I could use to have a more realistic representation or a better tangent handling for some points.
Does anyone have an idea to improve my code below ?
# Libraries
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# Some data to play with
df = pd.DataFrame({'measure':[10, -5, 15,20,20, 20,15,5,10], 'angle':[0,45,90,135,180, 225, 270, 315,360]})
# The few lines I would like to avoid...
angles = [y/180*np.pi for x in [np.arange(x, x+45,5) for x in df.angle[:-1]] for y in x]
values = [y for x in [np.linspace(x, df.measure[i+1], 10)[:-1] for i, x in enumerate(df.measure[:-1])] for y in x]
angles.append(360/180*np.pi)
values.append(values[0])
# Initialise the spider plot
ax = plt.subplot(polar=True)
# Plot data
ax.plot(df.angle/180*np.pi, df['measure'], linewidth=1, linestyle='solid', label="Spider chart")
ax.plot(angles, values, linewidth=1, linestyle='solid', label='what I want')
ax.legend()
# Fill area
ax.fill(angles, values, 'b', alpha=0.1)
plt.show()
the result is below, I want something similar to the orange line with some kind of spline to avoid sharp corners I currently get
I have a solution that is a patchwork of other solutions. It needs to be cleaned and optimized, but it does the job !
Comments and improvements are always welcome, see below
# https://stackoverflow.com/questions/33962717/interpolating-a-closed-curve-using-scipy
from scipy import interpolate
x=df.measure[:-1] * np.cos(df.angle[:-1]/180*np.pi)
y=df.measure[:-1] * np.sin(df.angle[:-1]/180*np.pi)
x = np.r_[x, x[0]]
y = np.r_[y, y[0]]
# fit splines to x=f(u) and y=g(u), treating both as periodic. also note that s=0
# is needed in order to force the spline fit to pass through all the input points.
tck, u = interpolate.splprep([x, y], s=0, per=True)
# evaluate the spline fits for 1000 evenly spaced distance values
xi, yi = interpolate.splev(np.linspace(0, 1, 1000), tck)
def cart2pol(x, y):
rho = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
return(rho, phi)
# Initialise the spider plot
plt.figure(figsize=(12,8))
ax = plt.subplot(polar=True)
# Plot data
ax.plot(df.angle/180*np.pi, df['measure'], linewidth=1, linestyle='solid', label="Spider chart")
ax.plot(angles, values, linewidth=1, linestyle='solid', label='Interval linearisation')
ax.plot(cart2pol(xi, yi)[1], cart2pol(xi, yi)[0], linewidth=1, linestyle='solid', label='Smooth interpolation')
ax.legend()
# Fill area
ax.fill(angles, values, 'b', alpha=0.1)
plt.show()

Some Data Points not Appearing on PyPlot in Python

I am trying to plot a chart that shows the Observation data points, along with the corresponding prediction.
However, as I am plotting, the red Observation dots are not appearing on my plot; and I am unsure as to why.
They do appear when I run the following in another line:
fig = plt.figure(figsize = (20,6))
plt.plot(testY, 'r.', markersize=10, label=u'Observations')
plt.plot(predictedY, 'b-', label=u'Prediction')
But the code that I am using to plot does not allows them to show up:
def plotGP(testY, predictedY, sigma):
fig = plt.figure(figsize = (20,6))
plt.plot(testY, 'r.', markersize=10, label=u'Observations')
plt.plot(predictedY, 'b-', label=u'Prediction')
x = range(len(testY))
plt.fill(np.concatenate([x, x[::-1]]), np.concatenate([predictedY - 1.9600 * sigma, (predictedY + 1.9600 * sigma)[::-1]]),
alpha=.5, fc='b', ec='None', label='95% confidence interval')
subset = results_dailyData['2010-01':'2010-12']
testY = subset['electricity-kWh']
predictedY = subset['predictedY']
sigma = subset['sigma']
plotGP(testY, predictedY, sigma)
My current plot, where the red Observation points are not appearing.
The plot when I run the plotting code in it's own line. I'd like these dots and the blue line to appear in the plot above:
You may want to consider the following example, where the two cases with and without the fill function from the question are compared.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import pandas as pd
def plotGP(ax, testY, predictedY, sigma, showfill=False):
ax.set_title("Show fill {}".format(showfill))
ax.plot(testY, 'r.', markersize=10, label=u'Observations')
ax.plot(predictedY, 'b-', label=u'Prediction')
x = range(len(testY))
if showfill:
ax.fill(np.concatenate([x, x[::-1]]), np.concatenate([predictedY - 1.9600 * sigma, (predictedY + 1.9600 * sigma)[::-1]]),
alpha=.5, fc='b', ec='None', label='95% confidence interval')
x = np.linspace(-5,-2)
y = np.cumsum(np.random.normal(size=len(x)))
sigma = 2
df = pd.DataFrame({"y" : y}, index=x)
fig, (ax, ax2) =plt.subplots(2,1)
plotGP(ax,df.y, df.y, sigma, False)
plotGP(ax2, df.y, df.y, sigma, True)
plt.show()
As can be seen, the plot curves may sit at completely different positions in the diagram, which would depend on the index of the dataframe.

Graphing tan in matplotlib

I have the following code:
from mpl_toolkits.axes_grid.axislines import SubplotZero
from matplotlib.transforms import BlendedGenericTransform
import matplotlib.pyplot as plt
import numpy
if 1:
fig = plt.figure(1)
ax = SubplotZero(fig, 111)
fig.add_subplot(ax)
ax.axhline(linewidth=1.7, color="black")
ax.axvline(linewidth=1.7, color="black")
plt.xticks([1])
plt.yticks([])
ax.text(0, 1.05, 'y', transform=BlendedGenericTransform(ax.transData, ax.transAxes), ha='center')
ax.text(1.05, 0, 'x', transform=BlendedGenericTransform(ax.transAxes, ax.transData), va='center')
for direction in ["xzero", "yzero"]:
ax.axis[direction].set_axisline_style("-|>")
ax.axis[direction].set_visible(True)
for direction in ["left", "right", "bottom", "top"]:
ax.axis[direction].set_visible(False)
x = numpy.linspace(-1, 1, 10000)
ax.plot(x, numpy.tan(2*(x - numpy.pi/2)), linewidth=1.2, color="black")
plt.ylim(-5, 5)
plt.savefig('graph.png')
which produces this graph:
As you can see, not only is the tan graph sketched, but a portion of line is added to join the asymptotic regions of the tan graph, where an asymptote would normally be.
Is there some built in way to skip that section? Or will I graph separate disjoint domains of tan that are bounded by asymptotes (if you get what I mean)?
Something you could try: set a finite threshold and modify your function to provide non-finite values after those points. Practical code modification:
yy = numpy.tan(2*(x - numpy.pi/2))
threshold = 10000
yy[yy>threshold] = numpy.inf
yy[yy<-threshold] = numpy.inf
ax.plot(x, yy, linewidth=1.2, color="black")
Results in:
This code creates a figure and one subplot for tangent function. NaN are inserted when cos(x) is tending to 0 (NaN means "Not a Number" and NaNs are not plotted or connected).
matplot-fmt-pi created by k-donn(https://pypi.org/project/matplot-fmt-pi/) used to change the formatter to make x labels and ticks correspond to multiples of π/8 in fractional format.
plot formatting (grid, legend, limits, axis) is performed as commented.
import matplotlib.pyplot as plt
import numpy as np
from matplot_fmt_pi import MultiplePi
fig, ax = plt.subplots() # creates a figure and one subplot
x = np.linspace(-2 * np.pi, 2 * np.pi, 1000)
y = np.tan(x)
y[np.abs(np.cos(x)) <= np.abs(np.sin(x[1]-x[0]))] = np.nan
# This operation inserts a NaN where cos(x) is reaching 0
# NaN means "Not a Number" and NaNs are not plotted or connected
ax.plot(x, y, lw=2, color="blue", label='Tangent')
# Set up grid, legend, and limits
ax.grid(True)
ax.axhline(0, color='black', lw=.75)
ax.axvline(0, color='black', lw=.75)
ax.set_title("Trigonometric Functions")
ax.legend(frameon=False) # remove frame legend frame
# axis formatting
ax.set_xlim(-2 * np.pi, 2 * np.pi)
pi_manager = MultiplePi(8) # number= ticks between 0 - pi
ax.xaxis.set_major_locator(pi_manager.locator())
ax.xaxis.set_major_formatter(pi_manager.formatter())
plt.ylim(top=10) # y axis limit values
plt.ylim(bottom=-10)
y_ticks = np.arange(-10, 10, 1)
plt.yticks(y_ticks)
fig
[![enter image description here][1]][1]plt.show()

Categories