Matplotlib Hover Text - python

I created this tree map using Matplotlib and Squarify. Is there a way to display information about each axes when the mouse hovers over the axis?

The mplcursors library can be used to create custom annotations while hovering. Here is an example with a tree map:
import matplotlib.pyplot as plt
import matplotlib as mpl
import squarify
import mplcursors
sizes = [5, 20, 30, 25, 10, 12]
sumsizes = sum(sizes)
labels = ['A', 'B', 'C', 'D', 'E', 'F']
cmap = plt.cm.get_cmap('Greens')
norm = plt.Normalize(vmin=min(sizes), vmax=max(sizes))
colors = [cmap(norm(s)) for s in sizes]
squarify.plot(sizes=sizes, label=labels, color=colors)
plt.colorbar(plt.cm.ScalarMappable(cmap=cmap, norm=norm))
cursor = mplcursors.cursor(hover=True)
cursor.connect("add", lambda sel: sel.annotation.set_text(
f"ID:{sel.target.index} '{labels[sel.target.index]}'\nSize:{sizes[sel.target.index]} ({sizes[sel.target.index] * 100.0 / sumsizes:.1f} %)"))
plt.show()

Related

Line chart to surface chart

[UPDATE: Sorry for not providing the piece where the author of the codes create example data. I have updated the codes]
I found an example of a 3D mesh line chart that satisfied what I need (colouring change with level on z dimension). However, instead of line, I want surface plot. How can I change the codes to have the 3d surface plot?
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from matplotlib.cm import get_cmap
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.font_manager import FontProperties
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from mpl_toolkits.mplot3d.art3d import Line3DCollection
index_returns = np.random.normal(loc=1e-4, scale=5e-3, size=(783, 9))
index_returns = np.vstack((np.zeros(shape=(1, 9)) + 100, index_returns))
index_prices = np.cumprod(1 + index_returns, axis=0)
window = 261
df = np.zeros(shape=(index_prices.shape[0]-window, 9))
for i in range(window, index_prices.shape[0], 1):
df[i-window] = (index_prices[i]/index_prices[i-window]) - 1
index = pd.date_range('2019-01-01', periods=index_prices.shape[0]-window, freq='B')
columns = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
df = pd.DataFrame(df, index=index, columns=columns)
# create the figure
fig = plt.figure(figsize=(14.4, 9))
ax = fig.add_subplot(111, projection='3d')
fig.patch.set_alpha(1)
# get the cmap to use
cmap = get_cmap('RdYlGn')
# get the slice based on data frame
current_slice = df.values[:261, :]
index_names = df.columns
index_dates = df.index
# list holding the lines
lines = []
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)
# Set the values used for colormapping
lc.set_array(z)
lc.set_linewidth(2)
lc.set_color(cmap(z[-1] * 2.5 + 0.5))
lc.set_label(index_names[i])
lines.append(ax.add_collection(lc))
# add the grids
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
ax.set_zlabel('Rolling Equity 1Y', labelpad=10)
ax.set_zlim(-0.39, 0.39)
ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})
ax.set_xlabel('Date', labelpad=30)
ax.set_xlim(0, current_slice.shape[0]-1)
ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.set_yticks(np.arange(current_slice.shape[1]))
ax.set_yticklabels([index_names[i] for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})
# show the plot
plt.show()

How to add logo to below of the x axis in the matplotlib charts

I want to add logo to the bottom right of the chart using matplotlib.
My logo is not fitting in the proper position.
my logo source="https://raw.githubusercontent.com/cldougl/plot_images/add_r_img/vox.png"
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as image
import matplotlib.pyplot as plt#
from matplotlib.offsetbox import OffsetImage
fig, ax = plt.subplots()
logo=image.imread("https://raw.githubusercontent.com/cldougl/plot_images/add_r_img/vox.png")
# You have to add your own logo, this is in my own folder
addLogo = OffsetImage(logo, zoom=0.4)
#addLogo.set_zorder(100)
addLogo.set_offset((250,-25)) # pass the position in a tuple
ax.add_artist(addLogo)
height = [6, 10, 5, 18, 45]
bars = ('A', 'B', 'C', 'D', 'E')
y = np.arange(len(bars))
plt.bar(y, height, color=(0.2, 0.4, 0.6, 0.6))
In jupyter notebook logo is like mirror.

Missing labels in matplotlib pie chart

I try to plot a pie chart using Python 3 Matplotlib v2.2.2-4build1 on Ubuntu 18.10. Everything seems to be ok except labels - they are missing. Tried to add it according to official documentation (https://matplotlib.org/api/_as_gen/matplotlib.pyplot.pie.html), tried to use an example from the web (https://pythonspot.com/matplotlib-pie-chart/) - same result, no labels.
Here is a simplified version of my code:
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sum = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sum))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="w"))
plt.show()
Here is what I see - no labels:
Tried to use a tuple instead of a list - same thing.
Could anybody help me?
You might want to make the color of your labels non-white on a white background :)
Also using sum as a variable name overwrites the function, so your're better off choosing something else.
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sumT = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sumT))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="k"))
plt.show()
Or if you want the labels to be inside:
import numpy as np
import matplotlib.pyplot as plt
import sys
def func(pct, allvals):
absolute = int(pct/100.*sum(allvals))
return "{:.1f}%)".format(pct)
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sumT = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sumT))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, autopct=lambda pct: func(pct,
values), textprops=dict(color="w"))
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
colors=['yellow','blue','red','pink','green']
plt.pie(values,labels=headers,
colors=colors,autopct='%1.2f%%',
shadow=True,startangle=90)
plt.title('pie chart')
plt.show()
Adding the plt.legend() statement before the plt.show() will do the job.
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sum))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="w"))
plt.legend()
plt.show()

How to change the text color of font in legend?

Is there a way to change the font color of the legend in a matplotlib plot?
Specially in occasions where the background of the plot is dark, the default black text in the legend is hard or impossible to read.
call Legend.get_texts() will get a list of Text object in the legend object:
import pylab as pl
pl.plot(randn(100), label="randn")
l = legend()
for text in l.get_texts():
text.set_color("red")
As of matplotlib version 3.3.0, you can now directly use the keyword argument labelcolor in matplotlib.pyplot.legend().
Example using the same color as the corresponding artist by setting labelcolor='linecolor':
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(4, 3))
plt.plot(np.arange(10), np.random.rand(10) * 0, '-', label='spam')
plt.plot(np.arange(10), np.random.rand(10) * 1, ':', label='ham')
plt.plot(np.arange(10), np.random.rand(10) * 2, 'o', label='eggs')
plt.legend(labelcolor='linecolor')
Example changing all text to white by setting labelcolor='w', e.g. for dark backgrounds:
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(4, 3))
plt.plot(np.arange(10), np.random.rand(10) * 0, '-', label='spam')
plt.plot(np.arange(10), np.random.rand(10) * 1, ':', label='ham')
plt.plot(np.arange(10), np.random.rand(10) * 2, 'o', label='eggs')
plt.legend(facecolor='k', labelcolor='w')
Because plt.setp broadcasts over iterables, you can also modify the text color in one line:
# Show some cool graphs
legend = plt.legend()
plt.setp(legend.get_texts(), color='w')
The last line will apply the colour to all elements in the collection of texts.
You can also do it with setp():
import pylab as plt
leg = plt.legend(framealpha = 0, loc = 'best')
for text in leg.get_texts():
plt.setp(text, color = 'w')
this method also allows you to set the fontsize and any number of other font properties in one line (listed here: http://matplotlib.org/users/text_props.html)
full example:
import pylab as plt
x = range(100)
y1 = range(100,200)
y2 = range(50,150)
fig = plt.figure(facecolor = 'k')
ax = fig.add_subplot(111, axisbg = 'k')
ax.tick_params(color='w', labelcolor='w')
for spine in ax.spines.values():
spine.set_edgecolor('w')
ax.plot(x, y1, c = 'w', label = 'y1')
ax.plot(x, y2, c = 'g', label = 'y2')
leg = plt.legend(framealpha = 0, loc = 'best')
for text in leg.get_texts():
plt.setp(text, color = 'w')
plt.show()
Anyone looking to change the color of the TITLE of the legend; seems to only be available through the side door:
leg._legend_title_box._text.set_color('#FFFFFF')

matplotlib: using a colormap to color table-cell background

I have a Pandas dataframe, and i want to plot it as matplotlib table. So far i have that part working with following code:
import numpy as np
randn = np.random.randn
from pandas import *
idx = Index(arange(1,11))
df = DataFrame(randn(10, 5), index=idx, columns=['A', 'B', 'C', 'D', 'E'])
vals = np.around(df.values,2)
fig = plt.figure(figsize=(15,8))
ax = fig.add_subplot(111, frameon=True, xticks=[], yticks=[])
the_table=plt.table(cellText=vals, rowLabels=df.index, colLabels=df.columns,
colWidths = [0.03]*vals.shape[1], loc='center')
table_props = the_table.properties()
table_cells = table_props['child_artists']
clm = cm.hot(vals)
for cell in table_cells:
cell.set_height(0.04)
# now i would like to set the backgroundcolor of the cell
At the end of this i would like to set the background-color of the cell according to the colormap - but how do i look it up in the clm array without an index?
Another question: can i somehow pass a format string to the table, so that it formats the text to 2 decimal places?
Any hints appreciated,
Andy
You can use plt.Normalize() to normalize your data, and the pass the normalized data to a Colormap object, for example plt.cm.hot().
plt.table() has an argument cellColours, which will be used to set the cells' background color accordingly.
Because cm.hot maps black to the minimal value, I increased the value range when creating the normalization object.
Here is the code:
from matplotlib import pyplot as plt
import numpy as np
randn = np.random.randn
from pandas import *
idx = Index(np.arange(1,11))
df = DataFrame(randn(10, 5), index=idx, columns=['A', 'B', 'C', 'D', 'E'])
vals = np.around(df.values,2)
norm = plt.Normalize(vals.min()-1, vals.max()+1)
colours = plt.cm.hot(normal(vals))
fig = plt.figure(figsize=(15,8))
ax = fig.add_subplot(111, frameon=True, xticks=[], yticks=[])
the_table=plt.table(cellText=vals, rowLabels=df.index, colLabels=df.columns,
colWidths = [0.03]*vals.shape[1], loc='center',
cellColours=colours)
plt.show()
The Andy's code working:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# sudo apt-get install python-pandas
# sudo apt-get install python-matplotlib
#
# python teste.py
from matplotlib import pyplot
from matplotlib import cm
import numpy
from pandas import *
idx = Index(numpy.arange(1, 11))
df = DataFrame(
numpy.random.randn(10, 5),
index=idx,
columns=['A', 'B', 'C', 'D', 'E']
)
vals = numpy.around(df.values, 2)
normal = pyplot.normalize(vals.min()-1, vals.max()+1)
fig = pyplot.figure(figsize=(15, 8))
ax = fig.add_subplot(111, frameon=True, xticks=[], yticks=[])
the_table = pyplot.table(
cellText=vals,
rowLabels=df.index,
colLabels=df.columns,
colWidths = [0.03]*vals.shape[1],
loc='center',
cellColours=pyplot.cm.hot(normal(vals))
)
pyplot.show()

Categories