ticker.FixedFormatter strategy to show the tics of the ax2 axis - python

Given 3 arrays:
X1 = 10.00, 30.10, 50.20, 70.30 ...
X2 = 1.9976433815311, 2.0109630315475, 2.0372702369401, 2.0665284897891 ...
Y = -0.0000008764356, -0.0000149459573, -0.0000326996870, -0.0000513717121 ...
There is a one-to-one correspondence between X1, X2 and Y, i.e.
the i-th element of X1 has an i-th associated value of X2 and a i-th value of Y.
The following is the plot of Y as a function of X1 (blue dots).
I would need the X2 axis to show all the corresponding X2 values for each X1 value.
Following the second answer on this post,
I have partially accomplished this thorugh the ticker.FixedFormatter strategy,
by which: the X2 array needs to be transformed to a tuple, and each element of this tuple needs to be a string.
As can be seen, not all red values of X2 are displayed for each value of X1, e.g. for X1 = 10.0 the corresponding X2 = 2.00 appears to be displaced.
I do not understand very well why this is occurring. I would appreciate if you could help me.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import sys
X1 = np.array([10.0000000000000, 30.1000000000000, 50.2000000000000, 70.3000000000000, 90.4000000000000, 110.5100000000000, 130.6100000000000, 150.7100000000000, 170.8100000000000, 190.9100000000000, 211.0100000000000, 231.1100000000000, 251.2100000000000, 271.3100000000000, 291.4100000000000, 311.5200000000000, 331.6200000000000 ])
Y = np.array([-0.0000008764356, -0.0000149459573, -0.0000326996870, -0.0000513717121, -0.0000652350399, -0.0000842214902, -0.0001003825474, -0.0001214363281, -0.0001376971422, -0.0001572720132, -0.0001971891337, -0.0002203926200, -0.0002747064193, -0.0003217228112, -0.0003764577474, -0.0004657478828, -0.0006232016207])
X2 = np.array([1.9976433815311, 2.0109630315475, 2.0372702369401, 2.0665284897891, 2.0995743328944, 2.1392386324550, 2.1789200955649, 2.2290243968267, 2.2872281293691, 2.3180577547912, 2.4100643103912, 2.4826981368480, 2.5794602952095, 2.6764219232389, 2.7963983991814, 2.9740753305878, 3.3107035136072])
##### Plotting:
fig, ax1 = plt.subplots()
ax1.plot(X1, Y, linestyle='--', marker="o", markersize=6, color='blue')
ax1.set_ylabel('Y', fontsize=20)
# Make the ax1-ticks and ax1-tick-labels match the line color (blue):
ax1.set_xlabel('X1', fontsize=20, color='blue')
plt.setp(ax1.get_xticklabels(), rotation='45') # rotate them
# Create a new axis:
ax2 = ax1.twiny()
# Make the ax2-ticks and ax2-tick-labels match the red color:
ax2.set_xlabel('X2', fontsize=20, color='red')
ax2.tick_params('x', colors='red')
fig.tight_layout()
ax2.set_xlim(1.9, 3.4)
ax1.set_ylim(-0.0007, 1.1e-5)
ax2.set_ylim(-0.0007, 1.1e-5)
ax1.grid()
# Convert all X2 elements to a list of strings:
X2_string_all = []
for i in X2:
aux = "%.2f" % i
X2_string = str(aux)
X2_string_all.append(X2_string)
# Convert that list into a tuple:
X2_string_all_tuple = tuple(X2_string_all)
ax1.xaxis.set_major_locator(ticker.FixedLocator((X1)))
ax2.xaxis.set_major_formatter(ticker.FixedFormatter((X2_string_all_tuple)))
plt.show()
Something like this would be the desired plot (the red lines that come across the plot are not necessary):

In your code ax2 does not know that it should behave exactly as ax1, just with different labels. So you need to tell it,
ax2.set_xlim(ax1.get_xlim())
Then just use the same tick locations for both axes,
ax1.set_xticks(X1)
ax2.set_xticks(X1)
and label the ticks of ax2 with values from X2
ax2.set_xticklabels(["%.2f" % i for i in X2])
Complete code:
import numpy as np
import matplotlib.pyplot as plt
X1 = np.array([10., 30.1, 50.2, 70.3, 90.4, 110.510, 130.610, 150.710, 170.810,
190.910, 211.010, 231.110, 251.210, 271.310, 291.410, 311.52, 331.62])
Y = np.array([-0.00000087, -0.0000149, -0.0000326, -0.0000513, -0.00006523, -0.0000842,
-0.0001003, -0.0001214, -0.00013769, -0.0001572, -0.0001971, -0.0002203,
-0.00027470, -0.0003217, -0.0003764, -0.0004657, -0.00062320])
X2 = np.array([1.997, 2.0109, 2.0372, 2.0665, 2.099, 2.1392, 2.1789, 2.2290,
2.287, 2.3180, 2.4100, 2.4826, 2.579, 2.6764, 2.7963, 2.9740, 3.310])
##### Plotting:
fig, ax1 = plt.subplots()
ax1.grid()
ax2 = ax1.twiny()
ax1.plot(X1, Y, linestyle='--', marker="o", markersize=6, color='blue')
ax1.set_ylabel('Y', fontsize=20)
ax1.set_xlabel('X1', fontsize=20, color='blue')
plt.setp(ax1.get_xticklabels(), rotation='45') # rotate them
ax2.set_xlabel('X2', fontsize=20, color='red')
plt.setp(ax2.get_xticklabels(), rotation='45', color='red')
# Set xlimits of ax2 the same as ax1
ax2.set_xlim(ax1.get_xlim())
# Set ticks at desired position
ax1.set_xticks(X1)
ax2.set_xticks(X1)
# Label ticks of ax2 with values from X2
ax2.set_xticklabels(["%.2f" % i for i in X2])
fig.tight_layout()
plt.show()

Related

Specify values on x axis for a mathplotlib.pyplot histogram

Given a certain dataset, I would like to create three histograms in one plot. The data (just a small snippet of a huge dataset, which would break the mold) looks like this:
x, y1, y2, y3
2.0466115, 0, 0, 0
2.349824, 0, 0, 0
2.697959, 0, 0, 0
3.097671, 0.195374, 0.191008, 0.167979
3.5566025, 0.522926, 0.511492, 0.426324
4.083526, 0.691916, 0.6774083,0.5790586666666666
4.688515, 0.8181206,0.801901, 0.6795873333333334
5.3831355, 0.8489766,0.833376, 0.707486
6.1806665, 0.809022, 0.795524, 0.6750806666666667
All my x values are the same, y1, y2 and y3 represent the three different y values. I'm creating a seperate list for each column and pass them as an argument for pyplot.hist. You can see my code here:
import numpy as np
from matplotlib import pyplot
from excel_to_csv import coordinates
y1 = coordinates(1) #another method, which creates the list out of the column
y2 = coordinates(2)
y3 = coordinates(3)
bins = np.linspace(0, 10, 150)
pyplot.hist(y1, bins, alpha=0.5, label='y1')
pyplot.hist(y2, bins, alpha=0.5, label='y2')
pyplot.hist(y3, bins, alpha=0.5, label='y3')
pyplot.legend(loc='upper right')
pyplot.show()
This code results in the following plot (regarding the actual dataset):
As far as I researched, you creating bins for the range of the x axis. But instead of doing so, I would like to put there my x values.
My goal is the histogram looking like this, but as a histogram (once again - regarding the huge dataset):
You can use np.histogram and then plot the values of the histogram:
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data
y1 = np.random.normal(3,1,10000)
y2 = np.random.normal(5,1,10000)
y3 = np.random.normal(7,1,10000)
bins = np.linspace(0, 10, 150)
x = np.linspace(0,10000,149)
# Plot regular histograms
plt.figure()
plt.hist(y1, bins, alpha=0.5, label='y1')
plt.hist(y2, bins, alpha=0.5, label='y2')
plt.hist(y3, bins, alpha=0.5, label='y3')
plt.ylabel('Frequency')
plt.xlabel('Bins')
plt.legend(loc='upper right')
plt.show()
# Compute histogram data
h1 = np.histogram(y1, bins)
h2 = np.histogram(y2, bins)
h3 = np.histogram(y3, bins)
# Compute bin average
bin_avg = bins[0:-1] + bins[1] - bins[0]
# Plot histogram data as a line with markers
plt.figure()
plt.plot(bin_avg, h1[0], alpha=0.5, label='y1', marker='o')
plt.plot(bin_avg, h2[0], alpha=0.5, label='y2', marker='o')
plt.plot(bin_avg, h3[0], alpha=0.5, label='y3', marker='o')
plt.ylabel('Frequency')
plt.xlabel('Bins')
plt.legend(loc='upper right')
plt.show()
It wouldn't make sense to plot the binned data versus x because once the data has been transformed by the histogram the relationship it had with x is no longer the same.

Two lines share an invisible xdata in matplotlib with different xticks

I have two sets of arrays x1, y1, t1 and x2, y2, t1 -- x data, y data and time measurement.
I would like to plot two these sets as lines with time as an x argument in plot(), so that lines are aligned with respect to time precedence of events.
However, I would also like to see the corresponding x1 and x2 on the plot in a form of xlabels (say at the top and at the bottom of the plot), as well as have two scales for y values (i.e. on the left and on the right of the figure).
import numpy as np
t1 = np.linspace(0, 10, 10)
y1 = np.arange(10)
x1 = (np.cumsum(np.random.rand(10)) * 1000000000).astype(int)
x1 = (x1 / 100000).astype(int) * 10
x2 = (np.cumsum(np.random.rand(10)) * 1000000000).astype(int)
x2 = (x2 / 1000000).astype(int)
y2 = 2 * np.arange(10)
t2 = np.linspace(0, 10, 10) + 2
from matplotlib import pyplot as plt
fig, ax1 = plt.subplots()
ax1.plot(t1, y1)
ax1.set_ylabel("y1 label")
ax1.set_xticklabels(x1)
ax1.set_xlabel("x1 label")
ax2 = ax1.twinx()
ax2.plot(t2, y2, c='r')
ax2.set_ylabel("y2 label")
ax3 = ax2.twiny()
ax3.xaxis.set_ticks_position('top')
ax3.set_xticklabels(x2);
ax3.set_xlabel("x2 label")
The code produces
which is good, but has two problems:
xlabels are not aligned with the data: blue line on the plot starts at x1 ticklabel 104500, while x1[0] = 29380.
I an unable to apply sci format for the x1 and x2 ticks, i.e. the line
ax1.ticklabel_format(style='sci', axis='x', scilimits=(0,0))
fails with This method only works with the ScalarFormatter, which is reasonable, since I have replaced labels of ticks, not ticks themselves. On the other hand, I cannot assign x1 to xticks, since this will change limits of xaxis.
How could I overcome two these problems?

Trying to plot a system of linear equation using matplotlib in a 2D plane

As the title says, I am trying to plot a system of linear equations to get the intersection point of the 2 equations.
8a-b = 9
4a+9b = 7.
below is the code i have tried.
import matplotlib.pyplot as plt
from numpy.linalg import inv
import numpy as np
a = np.array([[8,-1],[4,9]])
b = np.array([9,7])
c = np.linalg.solve(a,b)
plt.figure()
# Set x-axis range
plt.xlim((-10,10))
# Set y-axis range
plt.ylim((-10,10))
# Draw lines to split quadrants
plt.plot([-10,-10],[10,10], linewidth=4, color='blue' )
#draw the equations
plt.plot(a[0][0],a[0][1], linewidth=2, color='red' )
plt.plot(a[1][0],a[1][1], linewidth=2, color='red' )
plt.plot(c[0],c[1], marker='x', color="black")
plt.title('Quadrant plot')
plt.show()
I get only the intersection point, but not the lines on the 2D plane as shown in the below graph.
I want something like this.
To plot the lines it's easiest if you rearrange your equations to in terms of b. This way 8a-b=9 becomes b=8a-9 and 4a+9b=7 becomes b=(7-4a)/9
It also looks like you were trying to draw the "axis" of the graph, I've fixed this in the code below too.
The following should do the trick:
import matplotlib.pyplot as plt
import numpy as np
a = np.array([[8,-1],[4,9]])
b = np.array([9,7])
c = np.linalg.solve(a,b)
plt.figure()
# Set x-axis range
plt.xlim((-10,10))
# Set y-axis range
plt.ylim((-10,10))
# Draw lines to split quadrants
plt.plot([-10, 10], [0, 0], color='C0')
plt.plot([0, 0], [-10, 10], color='C0')
# Draw line 8a-b=9 => b=8a-9
x = np.linspace(-10, 10)
y = 8 * x - 9
plt.plot(x, y, color='C2')
# Draw line 4a+9b=7 => b=(7-4a)/9
y = (7 - 4*x) / 9
plt.plot(x, y, color='C2')
# Add solution
plt.scatter(c[0], c[1], marker='x', color='black')
# Annotate solution
plt.annotate('({:0.3f}, {:0.3f})'.format(c[0], c[1]), c+0.5)
plt.title('Quadrant plot')
plt.show()
This gave me the following plot:
x1 = np.arange(-10, 10, 0.01) # between -10 and 10, 0.01 stepsize
y1 = 8*x1-9
x2 = np.arange(-10, 10, 0.01) # between -10 and 10, 0.01 stepsize
y2 = (7-4*x2)/9
This is the equations of your lines.
Now plot these using plt.plot(x1,y1) etc.
plt.figure()
# Set x-axis range
plt.xlim((-10,10))
# Set y-axis range
plt.ylim((-10,10))
# Draw lines to split quadrants
plt.plot([-10,-10],[10,10], linewidth=4, color='blue' )
plt.plot(x1,y1)
plt.plot(x2,y2)
#draw the equations
plt.plot(a[0][0],a[0][1], linewidth=2, color='red' )
plt.plot(a[1][0],a[1][1], linewidth=2, color='red' )
plt.plot(c[0],c[1], marker='x', color="black")
plt.title('Quadrant plot')
plt.show()

Draw lines connecting points between two separate one-D plots

As title, I am working on time-series alignment, and a visualization of the alignment result is desired.
To this end, I want to draw lines connecting "anchor points" generated by the alignment algorithm.
np.random.seed(5)
x = np.random.rand(10) # time-series 1
y = np.random.rand(20) # time-series 2
ap = np.array(([0, 4, 9], # the anchor points
[0, 9, 19]))
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.plot(x, 'r')
ax2.plot(y, 'g')
the anchor points ap in the example specify the one-to-one "mapping" between the indices of two time series x and y, i.e., x[0] is corresponding to y[0]; x[4] to y[9]; and x[9] to y[19]. The goal is to draw lines between two separate plot to show the result of the alignment.
To connect two subplots in matplotlib you may use a ConnectionPatch.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import ConnectionPatch
np.random.seed(5)
x = np.random.rand(21) # time-series 1
y = np.random.rand(21) # time-series 2
ap = np.array(([0, 5, 10], # the anchor points
[0,10, 20]))
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.plot(x, 'r')
ax2.plot(y, 'g')
ls = ["-","--"]
c = ["gold", "blue"]
for i, row in enumerate(ap):
for j, ind in enumerate(row):
px = (ind, x[ind])
py = (ind, y[ind])
con = ConnectionPatch(py,px, coordsA="data", coordsB="data",
axesA=ax2, axesB=ax1, linestyle=ls[i], color=c[i])
ax2.add_artist(con)
plt.show()
Thanks to #ImportanceOfBeingErnest, I identified the typo in the OP and achieved connecting indices between two series of different length:
np.random.seed(5)
x = np.random.rand(10)
y = np.random.rand(20)
ap = np.array(([0, 4, 9],
[0,9, 19]))
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex=ax1)
ax1.plot(x, 'r')
ax2.plot(y, 'g')
plt.setp(ax1.get_xticklabels(), visible=False)
for j in ap.T:
ax1.axvline(x=j[0], linestyle='--', color='k')
ax2.axvline(x=j[1], linestyle='--', color='k')
x_ind = (j[0], ax1.get_ylim()[0])
y_ind = (j[1], ax2.get_ylim()[1])
con = ConnectionPatch(y_ind, x_ind, coordsA="data", coordsB="data",
axesA=ax2, axesB=ax1, linewidth='1.5')
ax2.add_artist(con)
I know it is off the topic, but how to further truncate the blank part in order to make the range of x-axis fit the signal length, while maintain the actual ratio of the length of the two signals? Though sharex=ax1 shows the ratio of signal length, the blank part on the right of the top figure is annoying.

Interactively changing the alpha value of matplotlib plots

I've looked at the documentation, but I can't seem to figure out if this is possible -
I have a dataset, with x and y values and discrete z values. Multiple pairs of (x,y) share the same z value. What I want to do is when I mouseover one point with a particular z value, the alpha of all the points with the same z values goes to 1 - i.e., If all the alpha values are initially 0.5, I'd like only the points with the same z value to go to 1.
Here's a minimal working example to illustrate what I'm talking about :
#! /usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(100)
y = np.random.randn(100)
z = np.arange(0, 10, 1)
z = np.repeat(z, 10)
im = plt.scatter(x, y, c=z, alpha = 0.5)
plt.colorbar(im)
plt.show()
You can probably fake what you want to achieve using a second plot:
import numpy as np
import matplotlib.pyplot as plt
Z = np.zeros(1000, dtype = [("Z", int), ("P", float, 2)])
Z["P"] = np.random.uniform(0.0,1.0,(len(Z),2))
Z["Z"] = np.random.randint(0,50,len(Z))
def on_pick(event):
z = Z[event.ind[0]]['Z']
P = Z[np.where(Z["Z"] == z)]["P"]
selection_plot.set_data(P[:,0],P[:,1])
plt.draw()
fig = plt.figure(figsize=(10,10), facecolor='white')
fig.canvas.mpl_connect('pick_event', on_pick)
ax = plt.subplot(111, aspect=1)
ax.plot(Z['P'][:,0], Z['P'][:,1], 'o', color='k', alpha=0.1, picker=5)
selection_plot, = ax.plot([],[], 'o', color='black', alpha=1.0, zorder=10)
plt.show()

Categories

Resources