How to add a single marker in a bar graph - python

I have the following code generating a bar graph. However, for the last bar, I need a star marker to show that there is no data for the last bar, here in the graph it's number 10.
data = pd.read_csv('data.csv')
df = pd.DataFrame(data)
plt.figure(figsize=(3,2))
X = list(df.iloc[:, 0])
Y = list(df.iloc[:, 1])
Z= list(df.iloc[:, 2])
X_axis = np.arange(len(X))
plt.bar(X_axis - 0.2, Y, 0.4, label='Actual',color='#436bad')
plt.bar(X_axis + 0.2, Z, 0.4, label='Predicted',color='#c5c9c7')
plt.legend(loc=2, prop={'size': 6.5})
labels=['1','2','3','4','5','6','7','8','9','10']
plt.xticks(X,labels,rotation=60)
plt.xlabel("Node no")
plt.ylabel("Accuracy (%)")
plt.ylim(60,95)

You can use plt.text and set * where do you want, like below:
(Because I can't run your code. I send an example)
import matplotlib.pyplot as plt
import numpy as np
x = np.array([1,3,7])
y = [2, 3, 2]
z = [1, 2, 3]
plt.bar(x-0.1, y, width=0.2, color='b', align='center')
plt.bar(x+0.1, z, width=0.2, color='g', align='center')
labels=[1,2,3,4,5,6,7,8,9,10]
plt.xticks(range(1,11),labels,rotation=60)
str_x = [l for l in labels if not l in x]
for s_x in str_x:
plt.text(s_x, 0.1, '*', ha='center', fontsize=26)
Output:

Related

How to properly set labels in contourf subplots?

I am trying to get rid of these purple points on the picture below. Here is my code:
p_values = [0., 0.05, 0.25, 0.5, 1, 1.5, 2, 5, 10, np.inf]
xx, yy = np.meshgrid(np.linspace(-3, 3, num = 101),
np.linspace(-3, 3, num = 101))
fig, axes = plt.subplots(ncols = (len(p_values) + 1) // 2,
nrows = 2, figsize = (16, 7))
for p, ax in zip(p_values, axes.flat):
### BEGIN Solution (do not delete this comment)
z = np.linalg.norm([xx, yy], ord = p, axis = 0)
ax.contourf(yy, xx, z, 25, cmap = 'coolwarm')
ax.contour(yy, xx, z, [1], colors = 'fuchsia', linewidths = 3)
ax.set_title(f'p = {p}')
ax.legend([f'$x: |x|_{{{p}}} = 1$']);
### END Solution (do not delete this comment)
plt.show()
Which parameters should be specified in ax.legend() in order to plot the graph clear.
You could create the legend using an explicit handle. In this case the fuchsia colored line is stored as the last element of ax.collections. Creating the legend with only labels, when there were no "handles with labels" set, could be the cause of the weird purple dots.
import matplotlib.pyplot as plt
import numpy as np
p_values = [0., 0.05, 0.25, 0.5, 1, 1.5, 2, 5, 10, np.inf]
xx, yy = np.meshgrid(np.linspace(-3, 3, num=101),
np.linspace(-3, 3, num=101))
fig, axes = plt.subplots(ncols=(len(p_values) + 1) // 2,
nrows=2, figsize=(16, 7))
cmap = plt.get_cmap('magma').copy()
cmap.set_extremes(over='green', under='black', bad='turquoise')
for p, ax in zip(p_values, axes.flat):
### BEGIN Solution (do not delete this comment)
z = np.linalg.norm([xx, yy], ord=p, axis=0)
cnt = ax.contourf(yy, xx, z, 25, cmap='coolwarm')
ax.contour(yy, xx, z, [1], colors='fuchsia', linewidths=3)
ax.set_title(f'p = {p}')
ax.legend(handles=[ax.collections[-1]], labels=[f'$x: |x|_{{{p}}} = 1$'])
plt.colorbar(cnt, ax=ax)
### END Solution (do not delete this comment)
plt.tight_layout()
plt.show()

Remove plot outer axises, without removing the subplot axis

I'm trying to have the following plot to appear like the second plot.
Without the axises (vertical, horizontal) that have no meaning for this plot and range from 0 to 1.
This is the code I'm using to generate to plot:
import matplotlib.pyplot as plt
import numpy as np
x_lim = (0, 1)
y_lim = (0, 1)
z_lim = (0, 1)
list_points = [[0.3, 0.3, 0], [0.4, 0.4, 0], [0, 0, 0], [.1, .1, .5], [0.3, 0.3, .2]]
def plot_tracking_map():
"""
Visualize all grapes centers on a 3d map.
This function generates a plot that represents the TB in 3D.
"""
x_cors, y_cors, z_cors = [], [], []
for i in range(len(list_points)):
x_cor, y_cor, z_cor = list_points[i][0], list_points[i][1], list_points[i][2]
x_cors.append(x_cor)
y_cors.append(y_cor)
z_cors.append(z_cor)
fig, ax = plt.subplots(figsize=(12, 12))
ax = fig.add_subplot(projection='3d')
yy, zz = np.meshgrid(range(2), range(2))
xx = yy
s = ax.scatter(x_cors, y_cors, z_cors, s=400, marker='o') # x,y,z coordinates, size of each point, colors.
# controls the alpha channel. all points have the same value, ignoring their distance
s.set_edgecolors = s.set_facecolors = lambda *args: None
ax.title.set_text(f'Imgae number 1')
plt.show()
plot_tracking_map()
edit
I changed the lines
fig, ax = plt.subplots(figsize=(12, 12))
ax = fig.add_subplot(projection='3d')
to
fig, ax = plt.subplots(figsize=(12, 12), subplot_kw={'projection': '3d'})
And it solved the problem.

How do i extend trend line in matplotlib plot?

Here is a part of the plot that I have
I need to create TrendLine that would be extended to the 3th
quarter of this plot... I can's think of any solution.
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
fig = plt.figure(figsize=(20,20))
ax = fig.add_subplot()
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
ax.grid(True, which='both')
mean_line = ax.plot()
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(x,p(x),"r--")
plt.show()
I don't think reverse x and y would do the job, it would be limited to the poly1d that pass (0,0)
I think the extending method should be using the fitted line itself.
so a more general method is extend the x and use the poly1d(z) to calculate an extended line. z is description of the fitted line, so feeding x value to z would draw the line.
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
# make an xx that with from -20 to 20
#xx =np.array(x)
#xx = sorted(np.concatenate((-xx, xx), axis=0))
xx = [-20, 20] # also work
fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.subplot().spines['right'].set_color('none')
plt.subplot().spines['top'].set_color('none')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
#ax.grid(True, which='both')
plt.subplot().grid(True, which='both')
mean_line = ax.plot()
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(xx,p(xx),"r--")
plt.show()
if you zoomin near the (0,0), you should see it's not passing the origin point.
zoomed in near (0,0)
result image
I don't have any experience with trendlines, but I created a composite of existing x and y values with different signs and drew the following graph.
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot()
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
ax.grid(True, which='both')
mean_line = ax.plot()
# update
xx =np.array(x)
xx = sorted(np.concatenate((-xx, xx), axis=0))
yy =np.array(y)
yy = sorted(np.concatenate((-yy, yy), axis=0))
z = np.polyfit(xx, yy, 1)
p = np.poly1d(z)
plt.plot(xx,p(xx),"r--")
plt.show()

Connecting points to a central point on 3D scatter Python

I'm making a molecular diagram of a tetrahedral molecule where the three outer points(or atoms) need to be connected by lines to the central point.
From How can I connect points on a 3D scatter plot? I was able to connect the dots, but it produces the incorrect lines.
Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [1, 2, 1.2, 1.5, 1.5]
y = [1, 1.2, 2, 1.5, 1.5]
z = [.5, .5, .5, 1.2, 2]
a = []
b = []
c = []
for item in x:
a.append(float(item))
for item in y:
b.append(float(item))
for item in z:
c.append(float(item))
r = np.array(a)
s = np.array(b)
t = np.array(c)
ax.set_xlabel("x axis")
ax.set_ylabel("y axis")
ax.set_zlabel("z axis")
ax.scatter(r,s,zs = t, s=200)
ax.plot3D(r,s,z)
plt.show()
I'd like all the points to connect to the central point(x=1.5, y=1.5, z=1.2). Here is what this looks like so far:
If you do ax.plot3D(r,s,z) you are plotting a line joining the 5 points one after another. What you need is to plot a line from each point to the point you want.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [1, 2, 1.2, 1.5, 1.5]
y = [1, 1.2, 2, 1.5, 1.5]
z = [.5, .5, .5, 1.2, 2]
# Change the way you create the array.
# You can do it in several ways.
r = np.array(x, dtype=np.float)
s = np.array([float(i) for i in y])
t = np.array(z) * 1.0
ax.scatter(r,s,zs = t, s=200, label='True Position')
# Iterate over each point, and plot the line.
for x, y, z in zip(r, s, t):
ax.plot3D([x, 1.5], [y, 1.5], [z, 1.2], 'b')
plt.show()

Matplotlib Scatter plot [duplicate]

I am trying to make a scatter plot and annotate data points with different numbers from a list.
So, for example, I want to plot y vs x and annotate with corresponding numbers from n.
y = [2.56422, 3.77284, 3.52623, 3.51468, 3.02199]
z = [0.15, 0.3, 0.45, 0.6, 0.75]
n = [58, 651, 393, 203, 123]
ax = fig.add_subplot(111)
ax1.scatter(z, y, fmt='o')
Any ideas?
I'm not aware of any plotting method which takes arrays or lists but you could use annotate() while iterating over the values in n.
import matplotlib.pyplot as plt
y = [2.56422, 3.77284, 3.52623, 3.51468, 3.02199]
z = [0.15, 0.3, 0.45, 0.6, 0.75]
n = [58, 651, 393, 203, 123]
fig, ax = plt.subplots()
ax.scatter(z, y)
for i, txt in enumerate(n):
ax.annotate(txt, (z[i], y[i]))
There are a lot of formatting options for annotate(), see the matplotlib website:
In case anyone is trying to apply the above solutions to a .scatter() instead of a .subplot(),
I tried running the following code
import matplotlib.pyplot as plt
y = [2.56422, 3.77284, 3.52623, 3.51468, 3.02199]
z = [0.15, 0.3, 0.45, 0.6, 0.75]
n = [58, 651, 393, 203, 123]
fig, ax = plt.scatter(z, y)
for i, txt in enumerate(n):
ax.annotate(txt, (z[i], y[i]))
But ran into errors stating "cannot unpack non-iterable PathCollection object", with the error specifically pointing at codeline fig, ax = plt.scatter(z, y)
I eventually solved the error using the following code
import matplotlib.pyplot as plt
plt.scatter(z, y)
for i, txt in enumerate(n):
plt.annotate(txt, (z[i], y[i]))
I didn't expect there to be a difference between .scatter() and .subplot()
I should have known better.
In versions earlier than matplotlib 2.0, ax.scatter is not necessary to plot text without markers. In version 2.0 you'll need ax.scatter to set the proper range and markers for text.
import matplotlib.pyplot as plt
y = [2.56422, 3.77284, 3.52623, 3.51468, 3.02199]
z = [0.15, 0.3, 0.45, 0.6, 0.75]
n = [58, 651, 393, 203, 123]
fig, ax = plt.subplots()
for i, txt in enumerate(n):
ax.annotate(txt, (z[i], y[i]))
And in this link you can find an example in 3d.
You may also use pyplot.text (see here).
def plot_embeddings(M_reduced, word2Ind, words):
"""
Plot in a scatterplot the embeddings of the words specified in the list "words".
Include a label next to each point.
"""
for word in words:
x, y = M_reduced[word2Ind[word]]
plt.scatter(x, y, marker='x', color='red')
plt.text(x+.03, y+.03, word, fontsize=9)
plt.show()
M_reduced_plot_test = np.array([[1, 1], [-1, -1], [1, -1], [-1, 1], [0, 0]])
word2Ind_plot_test = {'test1': 0, 'test2': 1, 'test3': 2, 'test4': 3, 'test5': 4}
words = ['test1', 'test2', 'test3', 'test4', 'test5']
plot_embeddings(M_reduced_plot_test, word2Ind_plot_test, words)
I would love to add that you can even use arrows /text boxes to annotate the labels. Here is what I mean:
import random
import matplotlib.pyplot as plt
y = [2.56422, 3.77284, 3.52623, 3.51468, 3.02199]
z = [0.15, 0.3, 0.45, 0.6, 0.75]
n = [58, 651, 393, 203, 123]
fig, ax = plt.subplots()
ax.scatter(z, y)
ax.annotate(n[0], (z[0], y[0]), xytext=(z[0]+0.05, y[0]+0.3),
arrowprops=dict(facecolor='red', shrink=0.05))
ax.annotate(n[1], (z[1], y[1]), xytext=(z[1]-0.05, y[1]-0.3),
arrowprops = dict( arrowstyle="->",
connectionstyle="angle3,angleA=0,angleB=-90"))
ax.annotate(n[2], (z[2], y[2]), xytext=(z[2]-0.05, y[2]-0.3),
arrowprops = dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1))
ax.annotate(n[3], (z[3], y[3]), xytext=(z[3]+0.05, y[3]-0.2),
arrowprops = dict(arrowstyle="fancy"))
ax.annotate(n[4], (z[4], y[4]), xytext=(z[4]-0.1, y[4]-0.2),
bbox=dict(boxstyle="round", alpha=0.1),
arrowprops = dict(arrowstyle="simple"))
plt.show()
Which will generate the following graph:
For limited set of values matplotlib is fine. But when you have lots of values the tooltip starts to overlap over other data points. But with limited space you can't ignore the values. Hence it's better to zoom out or zoom in.
Using plotly
import plotly.express as px
import pandas as pd
df = px.data.tips()
df = px.data.gapminder().query("year==2007 and continent=='Americas'")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", text="country", log_x=True, size_max=100, color="lifeExp")
fig.update_traces(textposition='top center')
fig.update_layout(title_text='Life Expectency', title_x=0.5)
fig.show()
Python 3.6+:
coordinates = [('a',1,2), ('b',3,4), ('c',5,6)]
for x in coordinates: plt.annotate(x[0], (x[1], x[2]))
This might be useful when you need individually annotate in different time (I mean, not in a single for loop)
ax = plt.gca()
ax.annotate('your_lable', (x,y))
where x and y are the your target coordinate and type is float/int.
As a one liner using list comprehension and numpy:
[ax.annotate(x[0], (x[1], x[2])) for x in np.array([n,z,y]).T]
setup is ditto to Rutger's answer.

Categories