I had created a chart with values (LSMA5['Low']), I'm able to plot the chart, but I want to show the values at each point of the chart, how can I do that?
Here are the code:
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
plt.figure(figsize=(12.6,4.6))
plt.plot(stock_store['Close'], label='ABCsTock', alpha=0.35)
plt.plot(LSMA5['Low'], label='LSMA5', alpha=1, linewidth=1)
plt.title('ABCsTock')
plt.xlabel('Jan. 01,2018 - Jul. 30,2020')
plt.ylabel('Price')
plt.legend(loc='upper right')
plt.show()
Thanks with regards
JC
If I understand what you're trying to do, here's a way to do that (with synthetic data):
x_arr = np.arange(10)
y_arr = np.random.randint(0, 10, 10)
plt.plot(x_arr, y_arr)
# zip joins x and y coordinates in pairs
for x,y in zip(x_arr,y_arr):
label = "{:.2f}".format(y)
plt.annotate(label, # this is the text
(x,y), # this is the point to label
textcoords="offset points", # how to position the text
xytext=(0,10), # distance from text to points (x,y)
ha='center') # horizontal alignment can be left, right or center
The output is:
Related
I will start out by showing the plot I'm getting:
What I'm trying to do is to have the value of each position to display at the center of each square, that I managed to do, although, it is not visible because I change the map to be binary (black and white). However when I add the grid lines to have them look separated they intersect on top of the values, as shown in the image above.
The array I'm plotting is only 3x3. Therefore, I would like to see nine equal boxes in total with the values at the center of each respective box. Anyone has an idea on how to do this? Here is the piece of my script that does this.
fig, ax = plt.subplots()
ax.matshow(np.zeros((3,3)), cmap='binary')
ax.grid(which='major', color='black', linestyle='-', linewidth=1)
# To display the values
for (i, j), z in np.ndenumerate(ipcavg):
ax.text(j, i, '{:0.2f}'.format(z), ha='center', va='center')
plt.show()
Thanks!
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.matshow(np.zeros((3,3)), cmap='binary')
ax.grid(which='major', color='black', linestyle='-', linewidth=1)
# To display the values
ax.set_xlim([0,3])
ax.set_ylim([0,3])
for (i, j), z in [[(0,0),0],[(1,1),1],[(2,2),2],[(3,3),3],[(2,3),4],[(1,3),5],[(2,1),6],[(3,2),7],[(1,2),7],[(3,1),8]]:
ax.text(j-0.5, i-0.5, '{:0.2f}'.format(z), ha='center', va='center')
plt.show()
use set_xlim,set_ylim to draw correctly the boxes
substract from x,y half of box height and provide coordinates for upper rightcorner of box
Background
Using an CNN autoencoder, I observe the projection of the latent space of a dataset of images. I'd like to hover over the 2D scatter plot and display the corresponding image. I also have the images true labels and would like to have it as legend (color scatter points).
Setup
My original images are contained in a 3D array X_plot, my PCA reduced dataset is in X, and I have a series of labels corresponding to the images in y.
X_plot.shape = (n, 64, 64) # n images of 64x64
X.shape = (n, 2) # list of 2D coordinates for each image
y.shape = (n, ) # n labels
# Example code to reproduce
from matplotlib import pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import numpy as np
n = 20
num_classes = 4
X_plot = np.random.rand(n, 64, 64)
X = np.random.rand(n, 2)
y = np.random.randint(num_classes, size=n)
Current code
Scatter with image display on hovering
This is largely inspired from this answer on StackOverFlow.
# Split 2D coordinates into list of xs and ys
xx, yy = zip(*X)
# create figure and plot scatter
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(xx, yy, ls="", marker=".")
# create the annotations box
im = OffsetImage(X_plot[0,:,:], zoom=1, cmap='gray')
xybox=(50., 50.)
ab = AnnotationBbox(im, (0,0), xybox=xybox, xycoords='data',
boxcoords="offset points", pad=0.3, arrowprops=dict(arrowstyle="->"))
# add it to the axes and make it invisible
ax.add_artist(ab)
ab.set_visible(False)
def hover(event):
# if the mouse is over the scatter points
if line.contains(event)[0]:
# find out the index within the array from the event
ind, = line.contains(event)[1]["ind"]
# get the figure size
w,h = fig.get_size_inches()*fig.dpi
ws = (event.x > w/2.)*-1 + (event.x <= w/2.)
hs = (event.y > h/2.)*-1 + (event.y <= h/2.)
# if event occurs in the top or right quadrant of the figure,
# change the annotation box position relative to mouse.
ab.xybox = (xybox[0]*ws, xybox[1]*hs)
# make annotation box visible
ab.set_visible(True)
# place it at the position of the hovered scatter point
ab.xy =(xx[ind], yy[ind])
# set the image corresponding to that point
im.set_data(X_plot[ind,:,:])
else:
#if the mouse is not over a scatter point
ab.set_visible(False)
fig.canvas.draw_idle()
# add callback for mouse moves
fig.canvas.mpl_connect('motion_notify_event', hover)
plt.show()
Scatter with legend
If I want to display the 2D scatter with points colored and labeled with y, I use the following code:
fig = plt.figure()
ax = fig.add_subplot(111)
labels = np.unique(y)
for label in labels:
filtered_by_label = X[y == label]
ax.scatter(*zip(*filtered_by_label), s=12, marker='.', alpha=0.9, label=label)
ax.legend()
ax.axis('off')
Challenge
I can't get the two pieces of code above merged together. ax.plot doesn't seem to accept a legend list as argument. Using the labels loop in the 2nd sub-solution, I would need to create the line object that is used in the hover function. However, I looked into merging several of them without success.
Any tips? Thanks!
I found a workaround by overlaying my two plots.
In the following section (scatter with hover):
ax = fig.add_subplot(111)
line, = ax.plot(xx, yy, ls="", marker=".")
simply add the multiple scatter plots with legend.
ax = fig.add_subplot(111)
line, = ax.plot(xx, yy, ls="", marker="") # no marker for this one
labels = np.unique(y)
for label in labels:
filtered_by_label = X[y == label]
ax.scatter(*zip(*filtered_by_label), s=12, marker='.', alpha=0.9, label=label)
The line object is still accessible by the hover function, and points are displayed in color!
Hey I'm using rainbow text function, which can be found in here
in order to make y axis label have colors that match closest colors of the conosle names on y axis.
So currently I've came up with this code:
fig, ax= plt.subplots(figsize=(5,6)) #used to take care of the size
sns.barplot(x=gbyplat,y=gbyplat.index, palette='husl') #creating barplot
ax.set_ylabel('Publisher', color='deepskyblue', size=15, alpha=0.8) #setting labels
ax.set_xlabel('Number of titles published', color='slateblue', size=15, alpha=0.7)
ax.set_title('Titles per platform ranking', color='deeppink', size=17, alpha=0.6)
ax.set_xlim(0,2350) #setting limit for the plot
ax.set_xticks(np.arange(0, max(gbyplat), 250)) #ticks frequency
ax.annotate('newest', size=12, xy=(390, 13), xytext=(700, 13.3),
arrowprops=dict(arrowstyle="fancy")) #annotations on plot
ax.annotate('max', size=9, xy=(2230,0.3), bbox=dict(boxstyle="round", fc="w", alpha=0.5))
ax.plot(2161,0, 'o', color='cyan') #creating the cricle highlight for PS2 max
p = sns.color_palette("husl", len(gbyplat))
for i, label in enumerate(ax.get_yticklabels()):
label.set_color(p[i])
rainbow_text(0,5, "Pub lis her".split(),
[p[10],p[11],p[12]],
size=10)
However, the issue is that I have to manually set coordinates for newly produced 'Publisher' label. According to the function code i can pass ax argument which would automatically fit the label to the y axis (if I understood correctly). So how can I do that? And second question, is there a way to access ylabel coordinates (of the current y axis label 'Publisher')?
Thanks
One can set the text at the position where the ylabel would normally reside by first drawing the ylabel, obtaining its coordinates and then setting it to an empty string. One can then adapt the example rainbow text function to use the obtained coordinates.
It will still be very tricky to select the colors and coordinates such that the text will have exactly the color of the bars next to it. This probably involves a lot a trial and error.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import transforms
import seaborn as sns
l =list("ABCDEFGHIJK")
x = np.arange(1,len(l)+1)[::-1]
f, ax=plt.subplots(figsize=(7,4.5))
sns.barplot(x=x,y=l, palette='husl', ax=ax)
plt.xlabel('Number of titles published', color='slateblue', size=15, alpha=0.7)
p = sns.color_palette("husl", len(l))
for i, label in enumerate(ax.get_yticklabels()):
label.set_color(p[i])
def rainbow_text(x, y, strings, colors, ax=None, **kw):
if ax is None:
ax = plt.gca()
canvas = ax.figure.canvas
lab = ax.set_ylabel("".join(strings))
canvas.draw()
labex = lab.get_window_extent()
t = ax.transAxes
labex_data = t.inverted().transform((labex.x0, labex.y0- labex.height/2.))
ax.set_ylabel("")
for s, c in zip(strings, colors):
text = ax.text(labex_data[0]+x, labex_data[1]+y, s, color=c, transform=t,
rotation=90, va='bottom', ha='center', **kw)
text.draw(canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, y=ex.height, units='dots')
rainbow_text(0, 0.06, ["Pub", "lish", "er"],[p[6], p[5],p[4]],size=15)
plt.show()
I am trying to plot the following charts with Matpltlib:
I would like to have the colored dots at a constant distance from the bottom of the charts.
However as you can see they jump all over the place as their y coordinate is given in y value, and the y axis is different in each chart. Is there a way to define their y position in pixels from the x axis? Without having to resort to % of (top of the chart - bottom of the chart) would be ideal. Thanks!
You can plot the points in axes coordinates instead of data coordinates. Axes coordinates range from 0 to 1 (lower left corner to upper right corner).
In order to use axes coordinates, you need to supply Axes.transAxes to the plot's transform argument - also see the transformation tutorial.
Here is a minimal example:
import matplotlib.pyplot as plt
plt.plot([1,5,9], [456,894,347], "r-",
label="plot in data coordinates")
plt.plot([0.2,0.3,0.7], [0.2,0.2,0.5], "bo",
transform=plt.gca().transAxes, label="plot in axes coordinates")
plt.legend()
plt.show()
If you want to specify the horizontal coordinate in data coordinates, and the vertical one in axes coordinates, you can use a blended transformation,
matplotlib.transforms.blended_transform_factory(ax.transData, ax.transAxes)
This can be used as follows.
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
ax = plt.gca()
plt.plot([12,25,48], [456,894,347], "r-",
label="plot in data coordinates")
plt.plot([0.2,0.3,0.7], [0.2,0.2,0.5], "bo",
transform=ax.transAxes, label="plot in axes coordinates")
#blended tranformation:
trans = transforms.blended_transform_factory(ax.transData, ax.transAxes)
plt.plot([15,30,35], [0.75,0.25,0.5], "gs", markersize=12,
transform=trans, label="plot x in data-,\ny in axes-coordinates")
plt.legend()
plt.show()
This question already has answers here:
How to add value labels on a bar chart
(7 answers)
Closed 5 months ago.
I would like to add data labels to factor plots generated by Seaborn. Here is an example:
import pandas as pd
from pandas import Series, DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
titanic_df = pd.read_csv('train.csv')
sns.factorplot('Sex',data=titanic_df,kind='count')
How can I add the 'count' values to the top of each bar on the graph?
You could do it this way:
import math
# Set plotting style
sns.set_style('whitegrid')
# Rounding the integer to the next hundredth value plus an offset of 100
def roundup(x):
return 100 + int(math.ceil(x / 100.0)) * 100
df = pd.read_csv('train.csv')
sns.factorplot('Sex', data=df, kind='count', alpha=0.7, size=4, aspect=1)
# Get current axis on current figure
ax = plt.gca()
# ylim max value to be set
y_max = df['Sex'].value_counts().max()
ax.set_ylim([0, roundup(y_max)])
# Iterate through the list of axes' patches
for p in ax.patches:
ax.text(p.get_x() + p.get_width()/2., p.get_height(), '%d' % int(p.get_height()),
fontsize=12, color='red', ha='center', va='bottom')
plt.show()
You could do something even simpler
plt.figure(figsize=(4, 3))
plot = sns.catplot(x='Sex', y='count', kind='bar', data=titanic_df)
# plot.ax gives the axis object
# plot.ax.patches gives list of bars that can be access using index starting at 0
for i, bar in enumerate(plot.ax.patches):
h = bar.get_height()
plot.ax.text(
i, # bar index (x coordinate of text)
h+10, # y coordinate of text
'{}'.format(int(h)), # y label
ha='center',
va='center',
fontweight='bold',
size=14)
The above answer from #nickil-maveli is simply great.
This is just to add some clarity about the parameters when you are adding the data labels to the barplot (as requested in the comments by #user27074)
# loop through all bars of the barplot
for nr, p in enumerate(ax.patches):
# height of bar, which is basically the data value
height = p.get_height()
# add text to specified position
ax.text(
# bar to which data label will be added
# so this is the x-coordinate of the data label
nr,
# height of data label: height / 2. is in the middle of the bar
# so this is the y-coordinate of the data label
height / 2.,
# formatting of data label
u'{:0.1f}%'.format(height),
# color of data label
color='black',
# size of data label
fontsize=18,
# horizontal alignment: possible values are center, right, left
ha='center',
# vertical alignment: possible values are top, bottom, center, baseline
va='center'
)