Plot two lines in one graph with each line own y-values - python-3.x

Using Matplotlib, I would like to plot two lines in one graph, where both lines have their own axis.
Currently, I have:
x = [3, 5, 7, 9, 10, 11, 13, 15, 17, 19, 20, 21, 23, 25, 27, 30, 35, 40, 45, 50]
y1 = [0.658431,0.702574,0.727149,0.760198,0.746229,0.768321,0.763344,0.764400,0.759935,0.758930,0.769689,0.773518,0.764118,0.780918,0.767377,0.766301,0.779629,0.774025,0.773127,0.782209]
y2 = [0.008676, 0.014630, 0.021286, 0.025562, 0.018247, 0.026771, 0.036187, 0.025633, 0.031402, 0.031140, 0.031333, 0.027820, 0.020359, 0.033351, 0.032603, 0.027474, 0.025250, 0.023103, 0.030988, 0.026503]
plt.plot(x, y1, label = "line 1")
plt.plot(x, y2, label = "line 2")
plt.xlabel('x - axis')
plt.ylabel('y - axis')
plt.title('Y1 and Y2')
plt.legend()
Which results in:
However, both lines look relatively flat as they are represented on the same axis and have a different scale. Instead, I would like an y-axis on the left from 0.66 to 0.78 (to represent y1) and I would like an y-axis on the right between 0 and 0.05 (to represent y2). Then, y1's values can be read on the left axis and y2's values can be read on the right axis and the relative changes are illustrated clearer.
How can I do this?

Based on #rperezsoto's comment, I have the following working code:
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel('x-axis')
ax1.set_ylabel('AUC', color='blue')
ax1.plot(x, y1, color='blue')
ax1.tick_params(axis='y', labelcolor='blue')
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('Standard deviation', color='grey') # we already handled the x-label with ax1
ax2.plot(x, y2, color='grey')
ax2.tick_params(axis='y', labelcolor='grey')
fig.tight_layout() # otherwise the right y-label is slightly clipped
plt.title('Y1 and Y2')
plt.show()

Related

Is there some way can add label in legend in plot by one step?

My legend now shows,
I want to add my label in legend, from 0 to 7, but I don't want to add a for-loop in my code and correct each label step by step, my code like that,
fig, ax = plt.subplots()
ax.set_title('Clusters by OPTICS in 2D space after PCA')
ax.set_xlabel('First Component')
ax.set_ylabel('Second Component')
points = ax.scatter(
pca_2_spec[:,0],
pca_2_spec[:,1],
s = 7,
marker='o',
c = pred_pca_2_spec,
cmap= 'rainbow')
ax.legend(*points.legend_elements(), title = 'cluster')
plt.show()
Assuming pred_pca_2_spec is some np.array with values [0, 5, 10, 15, 20, 30, 35] to change the values of these to be in the range 0-7, simply divide (each element) by 5.
Sample Data:
import numpy as np
from matplotlib import pyplot as plt
np.random.seed(54)
pca_2_spec = np.random.randint(-100, 300, (100, 2))
pred_pca_2_spec = np.random.choice([0, 5, 10, 15, 20, 25, 30, 35], 100)
Plotting Code:
fig, ax = plt.subplots()
ax.set_title('Clusters by OPTICS in 2D space after PCA')
ax.set_xlabel('First Component')
ax.set_ylabel('Second Component')
points = ax.scatter(
pca_2_spec[:, 0],
pca_2_spec[:, 1],
s=7,
marker='o',
c=pred_pca_2_spec / 5, # Divide By 5
cmap='rainbow')
ax.legend(*points.legend_elements(), title='cluster')
plt.show()

Hue, colorbar, or scatterplot colors do not match in seaborn.scatterplot

Using an example from another post, I'm adding a color bar to a scatter plot. The idea is that both dot hue, and colorbar hue, should conform to the maximum and minimum possible, so that the colorbar can reflect the range of values in the hue:
x= [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200]
y= [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200]
z= [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 255]
df = pd.DataFrame(list(zip(x, y, z)), columns =['x', 'y', 'z'])
colormap=matplotlib.cm.viridis
#A continuous color bar needs to be added independently
norm = plt.Normalize(df.z.min(), df.z.max())
sm = plt.cm.ScalarMappable(cmap=colormap, norm=norm)
sm.set_array([])
fig = plt.figure(figsize = (10,8), dpi=300)
ax = fig.add_subplot(1,1,1)
sb.scatterplot(x="x", y="y",
hue="z",
hue_norm=(0,255),
data=df,
palette=colormap,
ax=ax
)
ax.legend(bbox_to_anchor=(0, 1), loc=2, borderaxespad=0., title='hue from sb.scatterplot')
ax.figure.colorbar(sm).set_label('hue from sm')
plt.xlim(0,255)
plt.ylim(0,255)
plt.show()
Note how the hue from the scatterplot, even with hue_norm, ranges up to 300. In turn, the hue from the colorbar ranges from 0 to 255. From experimenting with values in hue_norm, it seems that matplotlib always rounds it off so that you have a "good" (even?) number of intervals.
My questions are:
Is which one is showing an incorrect range: the scatterplot, the scatterplot legend, or the colorbar? And how to correct it?
How could you retrieve min and max hue from the scatterplot (in this case 0 and 300, respectively), in order to set them as maximum and minimum of the colorbar?
Do you really need to use seaborn's scatterplot(). Using a numerical hue is always quite messy.
The following code is much simpler and yields an unambiguous output
fig, ax = plt.subplots()
g = ax.scatter(df['x'],df['y'], c=df['z'], cmap=colormap)
fig.colorbar(g)

How to plot the xlabel in two lines of different colors?

I have two different parameters (lines y1 and y2), of different units which I want to plot in the same figure because their individual values are of similar magnitude. I therefore want to put their respective units (Unit y1 and Unit y2) in the xlabel in one row each and color each row after the color of the line. How can I do this?
import numpy as np
import matplotlib as plt
x1 = np.arange(0, 10, 1)
y1 = np.arange(10, 0, -1)
x2 = np.arange(11, 21, 1)
y2 = np.arange(0, 10, 1)
plt.figure()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.xlabel('Unit y1\n''Unit y2')
plt.show()
One way is to use plt.text to put the labels. While it is unclear how you want the labels to be positioned, I will answer both possible ways
Way 1
import matplotlib.pyplot as plt
# Rest of the code
fig, ax = plt.subplots()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.text(0.2, -0.15, 'Unit y1', color='blue', transform=ax.transAxes)
plt.text(0.7, -0.15, 'Unit y2', color='red', transform=ax.transAxes)
plt.show()
Way 2
fig, ax = plt.subplots()
plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'red')
plt.text(0.45, -0.15, 'Unit y1', color='blue', transform=ax.transAxes)
plt.text(0.45, -0.2, 'Unit y2', color='red', transform=ax.transAxes)
plt.show()

Can someone explain the different parameters in matplotlib.patches.arc?

I just want to understand the basic parameters and what they do specifically - width, height, angle, theta1, theta 2. I followed the official documentation and I understood what the centre is, but I don't get what the theta 1 or 2 does, or the angle does or what the length of the horizontal or vertical axis means.
I tried experimenting with the parameters using different numbers but failed to hit upon an accurate result.
I'm trying to create the arc of the 3-point area in the basketball court
The Arc type is a subclass of Ellipse, extended to add two values theta1 and theta2. The behaviour of angle is the same for both Ellipse and Arc and determines the angle at which the ellipse is drawn.
from matplotlib import pyplot as plt
from matplotlib.patches import Ellipse
fig = plt.figure(figsize=(2,5))
ax = fig.add_subplot(1,1,1)
ax.set_ylim(0, 50)
ax.set_xlim(0, 20)
ax.axis('off')
a = Ellipse((10, 45), 10, 3, 0, color='red', lw=1)
ax.add_patch(a)
a = Ellipse((10, 40), 10, 3, 10, color='red', lw=1)
ax.add_patch(a)
a = Ellipse((10, 35), 10, 3, 20, color='red', lw=1)
ax.add_patch(a)
a = Ellipse((10, 30), 10, 3, 30, color='red', lw=1)
ax.add_patch(a)
for a in range(0, 360, 40):
a = Ellipse((10, 20), 10, 3, a, color='red', lw=1, fc='none')
ax.add_patch(a)
This produces —
Note that for a perfect circle (an ellipse of equal height and width) this makes no difference (as a circle is rotationally symmetrical).
from matplotlib import pyplot as plt
from matplotlib.patches import Ellipse
fig = plt.figure(figsize=(2,4))
ax = fig.add_subplot(1,1,1)
ax.set_ylim(0, 40)
ax.set_xlim(0, 20)
ax.axis('off')
a = Ellipse((10, 25), 10, 10, 0, color='red', lw=1)
ax.add_patch(a)
a = Ellipse((10, 10), 10, 10, 45, color='red', lw=1)
ax.add_patch(a)
Both circles are the same.
The Arc documentation for the matplotlib.patches.Arc explains that theta 1 & 2 are —
theta1, theta2 : float, optional
Starting and ending angles of the arc in degrees. These values are relative to angle, .e.g. if angle = 45 and theta1 = 90 the absolute starting angle is 135. Default theta1 = 0, theta2 = 360, i.e. a complete ellipse.
The key statement there is "Default theta1 = 0, theta2 = 360, i.e. a complete ellipse." — these parameters are used to draw partial ellipses, to create an arc. theta1 is the angle (or position on) the ellipse at which to start drawing, and theta2 is when to stop. Note that the calculation of the ellipse is unaffected.
The following code draws a series of arcs which should make the logic apparent —
from matplotlib import pyplot as plt
from matplotlib.patches import Arc
fig = plt.figure(figsize=(2,5))
ax = fig.add_subplot(1,1,1)
ax.set_ylim(0, 50)
ax.set_xlim(0, 20)
ax.axis('off')
# A complete ellipse, using theta1=0, theta2=360.
a = Arc((10, 45), 10, 3, 0, 0, 360, color='red', lw=1)
ax.add_patch(a)
# Reduce theta2 to 350, last 10 deg of ellipse not drawn.
a = Arc((10, 40), 10, 3, 0, 0, 350, color='red', lw=1)
ax.add_patch(a)
# Rotate the ellipse (angle=90), theta1 & theta2 are relative to start angle & rotate too.
a = Arc((10, 30), 10, 3, 90, 0, 350, color='red', lw=1)
ax.add_patch(a)
# Rotate the ellipse (angle=180), as above.
a = Arc((10, 20), 10, 3, 180, 0, 350, color='red', lw=1)
ax.add_patch(a)
# Draw the top half of the ellipse (theta 0-180 deg).
a = Arc((10, 10), 10, 3, 0, 0, 180, color='red', lw=1)
ax.add_patch(a)
# Draw the bottom half of the ellipse (theta 180-360 deg).
a = Arc((10, 5), 10, 3, 0, 180, 360, color='red', lw=1)
ax.add_patch(a)
This produces the following image, with arcs drawn above going from top to bottom. Compare with the comments in the code for explanation.

How to center the grid of a plot on scatter points?

I have this scatter plot:
I'd like to move the grid in a way that each point (green square) would be surrounded by the grid's cells. For example:
The code to reproduce the plot:
import matplotlib.pyplot as plt
data = [24, 24, 24, 16, 16, 2, 2, 2]
x = list(range(0, len(data)))
y = list(range(0, 25))
plt.scatter(x, data, marker='s', c='g', s=100)
plt.yticks(y)
plt.xticks(x)
plt.grid(True)
plt.show()
Maybe something like the following meets the requirement. You can use the minor ticks for the grid and the major ticks for the labels.
import numpy as np
import matplotlib.pyplot as plt
data = [24, 24, 24, 16, 16, 2, 2, 2]
x = list(range(0, len(data)))
fig, ax = plt.subplots()
ax.scatter(x, data, marker='s', c='g', s=49)
ax.set_yticks(np.arange(25))
ax.set_yticks(np.arange(25+1)-0.5, minor=True)
ax.set_xticks(np.arange(len(data)))
ax.set_xticks(np.arange(len(data)+1)-0.5, minor=True)
ax.grid(True, which="minor")
ax.set_aspect("equal")
plt.show()

Resources