PyPlot Change Scatter Label When Points Overlap - python-3.x

I am graphing my predicted and actual results of an ML project using pyplot. I have a scatter plot of each dataset as a subplot and the Y values are elements of [-1, 0, 1]. I would to change the color of the points if both points have the same X and Y value but am not sure how to implement this. Here is my code so far:
import matplotlib.pyplot as plt
Y = [1, 0, -1, 0, 1]
Z = [1, 1, 1, 1, 1]
plt.subplots()
plt.title('Title')
plt.xlabel('Timestep')
plt.ylabel('Score')
plt.scatter(x = [i for i in range(len(Y))], y = Y, label = 'Actual')
plt.scatter(x = [i for i in range(len(Y))], y = Z, label = 'Predicted')
plt.legend()

I would simply make use of NumPy indexing in this case. Specifically, first plot all the data points and then additionally highlight only those point which fulfill the condition X==Y and X==Z
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
Y = np.array([1, 0, -1, 0, 1])
Z = np.array([1, 1, 1, 1, 1])
X = np.arange(len(Y))
# Labels and titles here
plt.scatter(X, Y, label = 'Actual')
plt.scatter(X, Z, label = 'Predicted')
plt.scatter(X[X==Y], Y[X==Y], color='black', s=500)
plt.scatter(X[X==Z], Z[X==Z], color='red', s=500)
plt.xticks(X)
plt.legend()
plt.show()

Related

How to show percentage change in bar plot in Python pyplot

here is a simple bar plot :
x = [1, 2, 3]
y = [10, 45, 23]
plt.bar(x, y)
I just want to show the percentage change from one bar to another. Maybe you can help. Thanks
You could use the bar_label function (matplotlib 3.4.2):
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [10, 45, 23]
bars = plt.bar(x, y, fc='crimson', ec='navy')
plt.bar_label(bars, [''] + [f'{(y1 - y0) / y0 * 100:+.2f}%' for y0, y1 in zip(y[:-1], y[1:])])
plt.margins(y=0.1)
plt.show()

How to improve cone - make the bases more like a circle?

I have a code for plotting a cone with some text and arrows like axes. How to change this code to get the bases of the cone looking like circles? Is the problem in the settings of margins? Or is it necessary to define circles in another way?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
FancyArrowPatch.draw(self, renderer)
nn = 400 # number of points along circle's perimeter
theta = np.linspace(0, 2*np.pi, nn)
rho = np.ones(nn)
# (x,y) represents points on circle's perimeter
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
figsize = (5, 5)
ax = plt.axes(projection='3d') # set the axes for 3D plot
ax.azim = -88 # y rotation (default=270)
ax.elev = 13 # x rotation (default=0)
# Low, high values of z for plotting 2 circles at different elevation
loz, hiz = -15, 15
# Plot two circles
ax.plot(x, y, hiz)
ax.plot(x, y, loz)
# Set some indices to get proper (x,y) for line plotting
lo1,hi1 = 15, 15+nn//2
lo2,hi2 = lo1+nn//2-27, hi1-nn//2-27
# Plot 3d lines using coordinates of selected points
ax.plot([x[lo1], x[hi1]], [y[lo1], y[hi1]], [loz, hiz])
ax.plot([x[lo2], x[hi2]], [y[lo2], y[hi2]], [loz, hiz])
eps = 0.005
ax.plot([0, 0], [0, 0], [0, 20]) # extend in z direction
ax.plot([0-eps, 0], [0-eps, -5], [0-eps, 0]) # extend in y direction
ax.plot([0, 1.3], [0, 0], [0, 0]) # extend in x direction
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, 16]) # v vector
ax.plot([0.63, 0.63], [-4, -4], [16, -0.005]) # vertical projection
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, -0.005]) # to the beginning
ax.scatter(0, 0, 20, marker=(3, 0, 0), s=100, clip_on=False)
ax.scatter(0, -5, 0, marker=(3, 0, 43), s=100, clip_on=False)
ax.scatter(1.3, 0, 0, marker=(3, 0, 30), s=100, clip_on=False)
ax.scatter(0.6, -4, 16, marker=(3, 0, 80), s=100, clip_on=False)
ax.scatter(0.6, -4, -0.005, marker=(3, 0, 0), s=100, clip_on=False)
a2 = Arrow3D([0.14, -0.515], [-5.581, 1.358], [14.73, 4.983], mutation_scale=20, arrowstyle="-|>", color="k", connectionstyle="arc3,rad=0.3")
ax.add_artist(a2)
ax.text3D(0.23, -5.23, 23.33, r'$A$')
ax.text3D(1.41, 1.29, -2.7, r'$B$')
ax.text3D(-0.31, 1.46, -12.6, r'$C$')
ax.text3D(0.4, -5.48, 17, r'$D$')
ax.text3D(0.64, 1.57, -9.95, r'$E$')
ax.text3D(-0.2, -5.5, 15.73, r'$F$')
# Hide axes
ax._axis3don = False
# Save the figure (.pdf)
margins = { # vvv margin in inches
"left" : 1 / figsize[0],
"bottom" : -2.45 / figsize[1],
"right" : 1 - 0.5 / figsize[0],
"top" : 1 + 1.8 / figsize[1]
}
fig.subplots_adjust(**margins)
plt.savefig('output.pdf')
plt.show()
From this code I got the following output:
The desired output is bases looking like a circle.
This is the view from above:
It is not a circle but an ellipse.
Desired shape of the cone:

How to autoscale y-axis for bargraph in matplotlib?

I need to autoscale the y-axis on my bargraph in matplotlib in order to display the small differences in values. The reason why it needs to be autoscaled instead of having a fixed limit is because the values will change depending on what the user inputs. I've tried yscale log, but that doesn't work for negative values. I've tried symlog, but the graph stays the same. This is my current code:
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = range(700, 710, 1)
fig, ax = plt.subplots()
ax.bar(x, y)
plt.show()
Plots are automatically scaled for the full range of the data provided to the API.
For a bar plot, the best option to display the differences in the values of the bars, is probably to set the ylim for vertical bars or xlim for horizontal bars.
negative data
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = range(-700, -750, -5)
fig, ax = plt.subplots(figsize=(7, 5))
ax.bar(x, y)
plt.ylim(min(y), max(y))
positive data
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = range(700, 750, 5)
fig, ax = plt.subplots(figsize=(7, 5))
ax.bar(x, y)
plt.ylim(min(y), max(y))
mixed data
If the data has a wide range of positive and negative values, there's probably not a good option, as you've noted symlog doesn't help the issue.
The best option may be to plot the positive and negative data separately.
Creating a mask does't work with a list, so convert the lists to numpy arrays.
import numpy as np
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [700, -700, 710, -710, 720, -720, 730, -730, 740, -740]
x = np.array(x)
y = np.array(y)
mask = y >= 0 # positive mask
pos_y = y[mask] # get the positive values
neg_y = y[~mask] # get the negative values; ~ is not
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(7, 5))
ax1.bar(x[mask], pos_y) # also mask x to plot the bar at the correct x-tick
ax1.set_title('Positive Values')
ax1.set_ylim(min(pos_y), max(pos_y))
ax1.set_xticks(range(0, 12)) # buffer the number of x-ticks, so the x-ticks of the two plots align.
ax2.bar(x[~mask], neg_y)
ax2.set_title('Negative Values')
ax2.set_ylim(min(neg_y), max(neg_y))
ax2.set_xticks(range(0, 12))
plt.tight_layout() # better spacing between the two plots

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()

Categorical x-axis errorbar plot in matplotlib

I'd like to plot errorbars with categorical X variable. The error bars (upper and lower) are on Y values only.
For example, the code
import numpy as np
import matplotlib.pyplot as plt
x = ["4", "10", "50"]
y = [3, 2, 1]
yerr = np.matrix([[1.5, 1.1, 0.9], [1.3, 1.2, 0.8]])
fig, ax = plt.subplots(1, 1)
ax.errorbar(x, y, yerr=yerr)
plt.show()
plt.close()
gives the following error:
ValueError: In safezip, len(args[0])=3 but len(args[1])=1
The error you get has nothing to do with categorical axis.
You just cannot use a matrix. Use a numpy array,
yerr = np.array([[1.5, 1.1, 0.9], [1.3, 1.2, 0.8]])
or simply a list, there is no need to use numpy here,
yerr = [[1.5, 1.1, 0.9], [1.3, 1.2, 0.8]]

Resources