secondary XY-axis in python matplotlib - python

Good morning,
is it possible in matplotlib to get a secondary xy axis in the same graph?
I am not looking for twinx() or twiny().
I have two data sets: x1, y1 and x2, y2, which i both want to plot in the same graph.
I would like to plot the first data set in the normal axes: ax1.plot(x1, y1).
The second y-axis should be located on the right and the second x-axis should be located on the top.
But how do i set this up?
Kind regards
Thomas

Taken from matplotlib secondary_axis docu:
import matplotlib.pyplot as plt
import numpy as np
import datetime
import matplotlib.dates as mdates
from matplotlib.ticker import AutoMinorLocator
dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6)
for k in range(240)]
temperature = np.random.randn(len(dates)) * 4 + 6.7
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(dates, temperature)
ax.set_ylabel(r'$T\ [^oC]$')
plt.xticks(rotation=70)
def date2yday(x):
"""Convert matplotlib datenum to days since 2018-01-01."""
y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
return y
def yday2date(x):
"""Return a matplotlib datenum for *x* days after 2018-01-01."""
y = x + mdates.date2num(datetime.datetime(2018, 1, 1))
return y
secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date))
secax_x.set_xlabel('yday [2018]')
def celsius_to_fahrenheit(x):
return x * 1.8 + 32
def fahrenheit_to_celsius(x):
return (x - 32) / 1.8
secax_y = ax.secondary_yaxis(
'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius))
secax_y.set_ylabel(r'$T\ [^oF]$')
def celsius_to_anomaly(x):
return (x - np.mean(temperature))
def anomaly_to_celsius(x):
return (x + np.mean(temperature))
# use of a float for the position:
secax_y2 = ax.secondary_yaxis(
1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')
plt.show()
Results in this:

Related

ValueError: x and y must have same first dimension, but have shapes (100,) and ((1,))

I'm trying to plot a simple function using python, numpy and matplotlib but when I execute the script it returns a ValueError described in the title.
This is my code:
"""Geometrical interpretation: In Python, plot the function y = f(x) = x**3 − (1/x)
and plot its tangent line at x = 1 and at x = 2."""
import numpy as np
import matplotlib.pyplot as plt
def plot(func):
plt.figure(figsize=(12, 8))
x = np.linspace(-100, 100, 100)
plt.plot(x, func, '-', color='pink')
plt.show()
plt.close()
plot(lambda x: x ** 3 - (1 / x))
Please send this beginer some help :)
This was a good effort actually. You only needed to add the y variable using y=func(x). And then plt.plot(x,y)...
so this works:
import numpy as np
import matplotlib.pyplot as plt
def plot(func):
plt.figure(figsize=(12, 8))
x = np.linspace(-100, 100, 100)
y = func(x)
plt.plot(x, y, '-', color='pink')
plt.show()
plt.close()
plot(lambda x: x ** 3 - (1 / x))
result:

FuncAnimation how to update text after each iteration

I am trying to create an animation of a Monte-Carlo estimation of the number pi, for each iteration I would like the numerical estimation to be in text on the plot, but the previous text is not removed and makes the values unreadable. I tried Artist.remove(frame) with no success. The plot is done with Jupiter Notebook.
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
#create necessary arrays
x = np.arange(0,N)
y = np.zeros(N)
#set initial points to zero
inHull = 0
def inCircle(point):
#the function is given a point in R^n
#returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
#iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
#determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
#we store areas in array y.
y[i] = (inHull*4)/(i+1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y)- 1, max(y)+0.5))
def update_plot(i):
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
ax.scatter(x[i], y[i], color='none')
Artist.remove(ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5))))
ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5)))
anim = FuncAnimation(fig,
update_plot,
frames=np.arange(0, len(x), data_skip),
init_func=init_func,
interval=20)
plt.show()
Thank you.
As you have already done in init_func, you should clear the plot in each iteration with ax.clear(). Then it is necessary to edit slighlty the plot function:
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
And finally you have to fix x axis limits in each iteration with ax.set_xlim(0, N).
Complete Code
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
# create necessary arrays
x = np.arange(0, N)
y = np.zeros(N)
# set initial points to zero
inHull = 0
def inCircle(point):
# the function is given a point in R^n
# returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
# iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
# determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
# we store areas in array y.
y[i] = (inHull*4)/(i + 1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
txt = ax.text(N*0.6, max(y) + 0.25, "")
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y) - 1, max(y) + 0.5))
def update_plot(i):
ax.clear()
ax.plot(x[:i + data_skip], y[:i + data_skip], color = 'k')
ax.scatter(x[i], y[i], color = 'none')
ax.text(N*0.6, max(y) + 0.25, "Estimation: " + str(round(y[i], 5)))
ax.set_xlim(0, N)
anim = FuncAnimation(fig,
update_plot,
frames = np.arange(0, len(x), data_skip),
init_func = init_func,
interval = 20)
plt.show()
Animation

How to convert it back to datetime format?

I wanted to perform arithmetic operations on dates so i converted these dates
idx_1 = 2017-06-07 00:00:00
idx_2 = 2017-07-27 00:00:00
to floats using,
x1 = time.mktime(idx_1.timetuple()) # returns float of dates
>>> 1496773800.0
x2 = time.mktime(idx_2.timetuple())
>>> 1501093800.0
y1 = 155.98
y2 = 147.07
Am using the following code to plot:
import datetime as dt
import time
import numpy as np
import matplotlib.pyplot as plt
x = [x1, x2]
y = [y1, y2]
Difference = x2 - x1 #this helps to end the plotted line at specific point
coefficients = np.polyfit(x, y, 1)
polynomial = np.poly1d(coefficients)
# the np.linspace lets you set number of data points, line length.
x_axis = np.linspace(x1, x2 + Difference, 3) # linspace(start, end, num)
y_axis = polynomial(x_axis)
plt.plot(x_axis, y_axis)
plt.plot(x[0], y[0], 'go')
plt.plot(x[1], y[1], 'go')
plt.show()
Which plots:
How to make matplotlib to plot the actual dates on x axis instead of floats?
Any kind of Help is Greatly Appreciated.
Starting with datetime objects you may use matplotlib's date2num and num2date functions to convert to and from numerical values. The advantage is that the numerical data is then understood by matplotlib.dates locators and formatters.
import datetime
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates
idx_1 = datetime.datetime(2017,06,07,0,0,0)
idx_2 = datetime.datetime(2017,07,27,0,0,0)
idx = [idx_1, idx_2]
y1 = 155.98
y2 = 147.07
x = matplotlib.dates.date2num(idx)
y = [y1, y2]
Difference = x[1] - x[0] #this helps to end the plotted line at specific point
coefficients = np.polyfit(x, y, 1)
polynomial = np.poly1d(coefficients)
# the np.linspace lets you set number of data points, line length.
x_axis = np.linspace(x[0], x[1] + Difference, 3) # linspace(start, end, num)
y_axis = polynomial(x_axis)
plt.plot(x_axis, y_axis)
plt.plot(x[0], y[0], 'go')
plt.plot(x[1], y[1], 'go')
loc= matplotlib.dates.AutoDateLocator()
plt.gca().xaxis.set_major_locator(loc)
plt.gca().xaxis.set_major_formatter(matplotlib.dates.AutoDateFormatter(loc))
plt.gcf().autofmt_xdate()
plt.show()

Python: Creating a plot inside of a for loop with two different variables

I am trying to plot a function with two parameters. In this case I'd like the function to plot with respect to "yy" in order for it to be in polar coordinates. When I run the program I get 10 figures rather than 1 single plot. Is there a reason this happens? Also, I'm not getting a plot at all.
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
for tval in xx:
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You need a little restructure:
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
import numpy as np
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
for tval in xx:
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You also should handle the division by zero error.

Matplotlib curve with arrow ticks

I was wondering if it is possible to plot a curve in matplotlib with arrow ticks.
Something like:
from pylab import *
y = linspace(0,10,0.01)
x = cos(y)
plot(x, y, '->')
which should come out with a curve made like this --->---->----> when x increases and like this ---<----<----< whenit decreases (and for y as well, of course).
EDIT:
Furthermore, the arrows should be inclined in the curve's direction (for example, 45 degrees for the y=x function)
It is possible to use the same strategy as in matplotlib streamplot function. Based on the example already given by hitzg:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import matplotlib.patches as mpatches
def add_arrow_to_line2D(
axes, line, arrow_locs=[0.2, 0.4, 0.6, 0.8],
arrowstyle='-|>', arrowsize=1, transform=None):
"""
Add arrows to a matplotlib.lines.Line2D at selected locations.
Parameters:
-----------
axes:
line: Line2D object as returned by plot command
arrow_locs: list of locations where to insert arrows, % of total length
arrowstyle: style of the arrow
arrowsize: size of the arrow
transform: a matplotlib transform instance, default to data coordinates
Returns:
--------
arrows: list of arrows
"""
if not isinstance(line, mlines.Line2D):
raise ValueError("expected a matplotlib.lines.Line2D object")
x, y = line.get_xdata(), line.get_ydata()
arrow_kw = {
"arrowstyle": arrowstyle,
"mutation_scale": 10 * arrowsize,
}
color = line.get_color()
use_multicolor_lines = isinstance(color, np.ndarray)
if use_multicolor_lines:
raise NotImplementedError("multicolor lines not supported")
else:
arrow_kw['color'] = color
linewidth = line.get_linewidth()
if isinstance(linewidth, np.ndarray):
raise NotImplementedError("multiwidth lines not supported")
else:
arrow_kw['linewidth'] = linewidth
if transform is None:
transform = axes.transData
arrows = []
for loc in arrow_locs:
s = np.cumsum(np.sqrt(np.diff(x) ** 2 + np.diff(y) ** 2))
n = np.searchsorted(s, s[-1] * loc)
arrow_tail = (x[n], y[n])
arrow_head = (np.mean(x[n:n + 2]), np.mean(y[n:n + 2]))
p = mpatches.FancyArrowPatch(
arrow_tail, arrow_head, transform=transform,
**arrow_kw)
axes.add_patch(p)
arrows.append(p)
return arrows
y = np.linspace(0, 100, 200)
x = np.cos(y/5.)
fig, ax = plt.subplots(1, 1)
# print the line and the markers in seperate steps
line, = ax.plot(x, y, 'k-')
add_arrow_to_line2D(ax, line, arrow_locs=np.linspace(0., 1., 200),
arrowstyle='->')
plt.show()
Also refer to this answer.
Try this:
import numpy as np
import matplotlib.pyplot as plt
y = np.linspace(0,100,100)
x = np.cos(y/5.)
# use masked arrays
x1 = np.ma.masked_array(x[:-1], np.diff(x)>=0)
x2 = np.ma.masked_array(x[:-1], np.diff(x)<=0)
# print the line and the markers in seperate steps
plt.plot(x, y, 'k-')
plt.plot(x1, y[:-1], 'k<')
plt.plot(x2, y[:-1], 'k>')
plt.show()

Categories

Resources