Does anybody know how to plot emojis in matplotlib while using windows? I've been struggling to find a solution as most out there seem to be specific for macOS.
Below is my current graph showing emojis plotted in a vector space, but as usual most do not show up.
Is there perhaps any fonts already installed with matplotlib that provide emoji support or will I need to install some backend solutions?
Code:
def display_pca_scatterplot(model, words=None, sample=0):
if words == None:
if sample > 0:
words = np.random.choice(list(model.vocab.keys()), sample)
else:
words = [ word for word in model.vocab ]
prop = FontProperties(fname='/usr/share/fonts/truetype/noto/Apple Color Emoji.ttc')
word_vectors = np.array([model[w] for w in words])
twodim = PCA().fit_transform(word_vectors)[:,:2]
sb.set_style("darkgrid")
plt.figure(figsize=(10,10))
plt.scatter(twodim[:,0], twodim[:,1]) #, edgecolors='w', color='w')
for word, (x,y) in zip(words, twodim):
plt.text(x+0.0, y+0.0, word, fontsize=20) #fontproperties=prop)
This seems to work for me , but apparently depends on default fonts (eg "Segoe UI Emoji") being installed:
plt.text(0,.5,'π π π π π π
π π€£ βΊοΈ π π',fontsize=20)
i have created a small library (imojify) to deal with that issue
from imojify import imojify
from matplotlib import pyplot as plt
from matplotlib.offsetbox import OffsetImage,AnnotationBbox
def offset_image(cords, emoji, ax):
img = plt.imread(imojify.get_img_path(emoji))
im = OffsetImage(img, zoom=0.08)
im.image.axes = ax
ab = AnnotationBbox(im, (cords[0], cords[1]), frameon=False, pad=0)
ax.add_artist(ab)
emjis = ['π', 'π€£', 'π', 'π','π', 'π₯°', 'π']
values =[30, 50, 15, 29, 15, 50, 12]
fig, ax = plt.subplots(figsize=(12,8))
ax.bar(range(len(emjis)), values, width=0.5,align="center")
ax.set_xticks(range(len(emjis)))
ax.set_xticklabels([])
ax.tick_params(axis='x', which='major', pad=26)
ax.set_ylim((0, ax.get_ylim()[1]+10))
for i, e in enumerate(emjis):
offset_image([i,values[i]+5], e, ax)
the library contains images for all emojis,
imojify.get_img_path(emoji) simply returns the path of the emoji image then you can use OffsetImage to add these images as labels
Related
I'm a MSc Student and I used to make graphs and plots with commercial packages like OriginPro, Excel and Matlab. Although these softwares provide a great user experience, there are some major disadvantages as they are specific OS dependent and, in general, very expensive.
Hence, I started to learn Python using matplotlib library with VS Code, however I'm having some problems with some library functions and statements that seems to be standard from matplotlib and numPy, but it doesnt work.
For example, I'm making some templates for scatter plots and I can't control minor ticks because it doesn't recognize the statements xaxix and yaxix:
Sample of the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, AutoMinorLocator
.
.
.
fig = plt.figure(figsize=(x_pixels/my_dpi, y_pixels/my_dpi), dpi=my_dpi)
ax = plt.scatter(x*format_x, y*format_y, s = size, alpha = transparency, color = color, label = legend_text)
.
.
.
# Major Ticks
plt.tick_params(axis = 'both', which = 'major', length = majorT_length, direction = majorT_direction, color = majorT_color, labelsize = label_size, top = 'on', right = 'on')
# Minor Ticks
plt.minorticks_on()
plt.tick_params(axis='both', which='minor', length = minorT_length, direction = minorT_direction, color = minorT_color, top = 'on', right = 'on')
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
# Figure Layout
plt.tight_layout()
plt.savefig(output_file, dpi=my_dpi, bbox_inches=borders)
plt.show()
and the Terminal show this error:
File "c:/Users/luagu/Desktop/Python Matplotlib Training/Scatter_Template.py", line 128, in <module>
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
AttributeError: 'PathCollection' object has no attribute 'yaxis'
What I'm doing wrong?
Thanks in advance!
You wrote ax = plt.scatter but your ax here is an artist returned by the scatter method, not an Axes object. What you want to do is:
plt.scatter(...)
...
ax = plt.gca()
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
UPDATE
Trying some more, I managed to run this code without error:
from matplotlib.pyplot import figure
dict = pd.DataFrame({"Return": mkw_returns, "Standard Deviation": mkw_stds})
dict.head()
#plt.annotate("Sharpe Ratio", xytext=(0.5,0.5), xy=(0.03,0.03) , arrowprops=dict(facecolor='blue', shrink=0.01, width=220)) # arrowprops={width = 3, "facecolor":
#dict.plot(x="Standard Deviation", y = "Return", kind="scatter", figsize=(10,6))
#plt.xlabel("Standard Deviations")
#plt.ylabel("log_Return YoY")
figure(num=None, figsize=(15, 10), dpi=100, facecolor='w', edgecolor='k')
plt.plot( 'Standard Deviation', 'Return', data=dict, linestyle='none', marker='o')
plt.xlabel("Standard Deviations")
plt.ylabel("log_Return YoY")
# Annotate with text + Arrow
plt.annotate(
# Label and coordinate
'This is a Test', xy=(0.01, 1), xytext=(0.01, 1), color= "r", arrowprops={"facecolor": 'black', "shrink": 0.05}
)
Which now works YaY, can anybody shed some light onto this issue? Im not so sure why it suddenly started working. Thank you :)
Also, how would I simply mark a point, instead of using the arrow?
Problem: Cannot figure out how to mark/select/highlight a specific point in my scatter graph
(Python 3 Beginner)
So my goal is to highlight one or more points in a scatter graph with some text by it or supplied by a legend.
https://imgur.com/a/VWeO1EH
(not enough reputation to post images, sorry)
dict = pd.DataFrame({"Return": mkw_returns, "Standard Deviation": mkw_stds})
dict.head()
#plt.annotate("Sharpe Ratio", xytext=(0.5,0.5), xy=(0.03,0.03) , arrowprops=dict(facecolor='blue', shrink=0.01, width=220)) # arrowprops={width = 3, "facecolor":
dict.plot(x="Standard Deviation", y = "Return", kind="scatter", figsize=(10,6))
plt.xlabel("Standard Deviations")
plt.ylabel("log_Return YoY")
The supressed "plt.annotate" would give an error as specified below.
Specifically i would like to select the sharpe ratio, but for now Im happy if I manage to select any point in the scatter graph.
Im truly confused how to work with matplotlib, so any help is welcomed
I tried the following solutions I found online:
I)
This shows a simple way to use annotate in a plot, to mark a specific point by an arrow.
https://www.youtube.com/watch?v=ItHDZEE5wSk
However the pd.dataframe environment does not like annotate and i get the error:
TypeError: 'DataFrame' object is not callable
II)
Since Im running into issues with annotate in a Data Frame environment, I looked at the following solution
Annotate data points while plotting from Pandas DataFrame
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import string
df = pd.DataFrame({'x':np.random.rand(10), 'y':np.random.rand(10)},
index=list(string.ascii_lowercase[:10]))
fig, ax = plt.subplots()
df.plot('x', 'y', kind='scatter', ax=ax, figsize=(10,6))
for k, v in df.iterrows():
ax.annotate(k, v)
However the resulting plot does not show any annotation what so ever when applied to my problem, besides this very long horizontal scroll bar
https://imgur.com/a/O8ykmeg
III)
Further, I stumbled upon this solution, to use a marker instead of an arrow,
Matplotlib annotate with marker instead of arrow
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
x=[1,2,3,4,5,6,7,8,9,10]
y=[1,1,1,2,10,2,1,1,1,1]
line, = ax.plot(x, y)
ymax = max(y)
xpos = y.index(ymax)
xmax = x[xpos]
# Add dot and corresponding text
ax.plot(xmax, ymax, 'ro')
ax.text(xmax, ymax+2, 'local max:' + str(ymax))
ax.set_ylim(0,20)
plt.show()
however the code does absolutely nothing, when applied to my situation like so
dict = pd.DataFrame({"Return": mkw_returns, "Standard Deviation": mkw_stds})
dict.head()
plt.annotate("Sharpe Ratio", xytext=(0.5,0.5), xy=(0.03,0.03) , arrowprops=dict(facecolor='blue', shrink=0.01, width=220)) # arrowprops={width = 3, "facecolor":
dict.plot(x="Standard Deviation", y = "Return", kind="scatter", figsize=(10,6))
plt.xlabel("Standard Deviations")
plt.ylabel("log_Return YoY")
ymax = max(y)
xpos = y.index(ymax)
xmax = x[xpos]
# Add dot and corresponding text
ax.plot(xmax, ymax, 'ro')
ax.text(xmax, ymax+2, 'local max:' + str(ymax))
ax.set_ylim(0,20)
plt.show()
IV)
Lastly, I tried a solution that apparently works flawlessly with an arrow in a pd.dataframe,
https://python-graph-gallery.com/193-annotate-matplotlib-chart/
# Library
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# Basic chart
df=pd.DataFrame({'x': range(1,101), 'y': np.random.randn(100)*15+range(1,101) })
plt.plot( 'x', 'y', data=df, linestyle='none', marker='o')
# Annotate with text + Arrow
plt.annotate(
# Label and coordinate
'This point is interesting!', xy=(25, 50), xytext=(0, 80),
# Custom arrow
arrowprops=dict(facecolor='black', shrink=0.05)
)
however running this code yields me the same error as above:
TypeError: 'DataFrame' object is not callable
Version:
import sys; print(sys.version)
3.7.1 (default, Dec 10 2018, 22:54:23) [MSC v.1915 64 bit (AMD64)]
Sorry for the WoT, but I thought its best to have everything I tried together in one post.
Any help is appreciated, thank you!
I think one solution is the following, as posted above as the "UPDATE":
UPDATE
Trying some more, I managed to run this code without error:
from matplotlib.pyplot import figure
dict = pd.DataFrame({"Return": mkw_returns, "Standard Deviation": mkw_stds})
dict.head()
#plt.annotate("Sharpe Ratio", xytext=(0.5,0.5), xy=(0.03,0.03) , arrowprops=dict(facecolor='blue', shrink=0.01, width=220)) # arrowprops={width = 3, "facecolor":
#dict.plot(x="Standard Deviation", y = "Return", kind="scatter", figsize=(10,6))
#plt.xlabel("Standard Deviations")
#plt.ylabel("log_Return YoY")
figure(num=None, figsize=(15, 10), dpi=100, facecolor='w', edgecolor='k')
plt.plot( 'Standard Deviation', 'Return', data=dict, linestyle='none', marker='o')
plt.xlabel("Standard Deviations")
plt.ylabel("log_Return YoY")
# Annotate with text + Arrow
plt.annotate(
# Label and coordinate
'This is a Test', xy=(0.01, 1), xytext=(0.01, 1), color= "r", arrowprops={"facecolor": 'black', "shrink": 0.05}
)
One question remains, how can I use a different marker or color and write about it in the legend instead?
Thanks in advance :)
Is it possible to use a font-awesome icon as a marker in a scatterplot with matplotlib?
Or is it possible to use it as a font and put the icon as a 'text'?
This question has originally been asked here, but has been closed for an unknown reason. Since I consider it a valid and useful problem, which hasn't been addressed anywhere on Stackoverflow and sure deserves an answer, I will simply ask it again.
FontAwesome is available from here.
It provides its icons as vector graphics and as well as as otf-font.
Use FontAwesome otf font
Matplotlib cannot natively read vector graphics, but it can load otf-fonts.
After downloading the FontAwesome package you can access the font via a matplotlib.font_manager.FontProperties object, e.g.
fp = FontProperties(fname=r"C:\Windows\Fonts\Font Awesome 5 Free-Solid-900.otf")
Create texts
The FontProperties can be the input for matplotlib text objects
plt.text(.6, .4, "\uf16c", fontproperties=fp)
Unfortunately, using the FontAwesome ligatures is not possible. Hence the individual symbols need to be accessed via their UTF8 key. This is a little cumbersome, but the cheatsheet can come handy here. Storing those needed symbols in a dictionary with a meaningful name may make sense.
Example:
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
fp1 = FontProperties(fname=r"C:\Windows\Fonts\Font Awesome 5 Brands-Regular-400.otf")
fp2 = FontProperties(fname=r"C:\Windows\Fonts\Font Awesome 5 Free-Solid-900.otf")
symbols = dict(cloud = "\uf6c4", campground = "\uf6bb", hiking = "\uf6ec",
mountain = "\uf6fc", tree = "\uf1bb", fish = "\uf578",
stackoverflow = "\uf16c")
fig, (ax, ax2) = plt.subplots(ncols=2, figsize=(6.2, 2.2), sharey=True)
ax.text(.5, .5, symbols["stackoverflow"], fontproperties=fp1, size=100,
color="orange", ha="center", va="center")
ax2.stackplot([0,.3,.55,.6,.65,1],[.1,.2,.2,.2,.2,.15],[.3,.2,.2,.3,.2,.2],
colors=["paleturquoise", "palegreen"])
ax2.axis([0,1,0,1])
ax2.text(.6, .4, symbols["mountain"], fontproperties=fp2, size=16, ha="center")
ax2.text(.09, .23, symbols["campground"], fontproperties=fp2, size=13)
ax2.text(.22, .27, symbols["hiking"], fontproperties=fp2, size=14)
ax2.text(.7, .24, symbols["tree"], fontproperties=fp2, size=14,color="forestgreen")
ax2.text(.8, .33, symbols["tree"], fontproperties=fp2, size=14,color="forestgreen")
ax2.text(.88, .28, symbols["tree"], fontproperties=fp2, size=14,color="forestgreen")
ax2.text(.35, .03, symbols["fish"], fontproperties=fp2, size=14,)
ax2.text(.2, .7, symbols["cloud"], fontproperties=fp2, size=28,)
plt.show()
Create markers
Creating a lot of texts like above is not really handy. To have the icons as markers would be nicer for certain applications. Matplotlib does have the ability to use utf symbols as markers, however, only through the mathtext functionality. Getting an otf font to be used as mathfont in matplotlib was unsuccessful in my trials.
An alternative is to create a matplotlib.path.Path from the symbol. This can be done via a matplotlib.textpath.TextToPath instance, which is unfortunately undocumented. The TextToPath has a method get_text_path taking a fontproperty and a string as input and returning the vertices and codes from which to create a Path. A Path can be used as a marker, e.g. for a scatter plot.
v, codes = TextToPath().get_text_path(fp, \uf6fc)
path = Path(v, codes, closed=False)
plt.scatter(..., marker=path)
Some example:
import numpy as np; np.random.seed(32)
from matplotlib.path import Path
from matplotlib.textpath import TextToPath
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
fp = FontProperties(fname=r"C:\Windows\Fonts\Font Awesome 5 Free-Solid-900.otf")
symbols = dict(cloud = "\uf6c4", campground = "\uf6bb", hiking = "\uf6ec",
mountain = "\uf6fc", tree = "\uf1bb", fish = "\uf578",
stackoverflow = "\uf16c")
fig, ax = plt.subplots()
def get_marker(symbol):
v, codes = TextToPath().get_text_path(fp, symbol)
v = np.array(v)
mean = np.mean([np.max(v,axis=0), np.min(v, axis=0)], axis=0)
return Path(v-mean, codes, closed=False)
x = np.random.randn(4,10)
c = np.random.rand(10)
s = np.random.randint(120,500, size=10)
plt.scatter(*x[:2], s=s, c=c, marker=get_marker(symbols["cloud"]),
edgecolors="none", linewidth=2)
plt.scatter(*x[2:], s=s, c=c, marker=get_marker(symbols["fish"]),
edgecolors="none", linewidth=2)
plt.show()
Adding to ImportanceOfBeingErnest's great answer: you can easily get the UTF keys by using the fontawesome Python package.
Install via pip:
pip install fontawesome
Usage:
import fontawesome as fa
# icons dict converts to correct utf-8 code:
fa.icons['thumbs-up']
Is there a way in matplotlib to partially specify the color of a string?
Example:
plt.ylabel("Today is cloudy.")
How can I show "today" as red, "is" as green and "cloudy." as blue?
I only know how to do this non-interactively, and even then only with the 'PS' backend.
To do this, I would use Latex to format the text. Then I would include the 'color' package, and set your colors as you wish.
Here is an example of doing this:
import matplotlib
matplotlib.use('ps')
from matplotlib import rc
rc('text',usetex=True)
rc('text.latex', preamble='\usepackage{color}')
import matplotlib.pyplot as plt
plt.figure()
plt.ylabel(r'\textcolor{red}{Today} '+
r'\textcolor{green}{is} '+
r'\textcolor{blue}{cloudy.}')
plt.savefig('test.ps')
This results in (converted from ps to png using ImageMagick, so I could post it here):
Here's the interactive version. Edit: Fixed bug producing extra spaces in Matplotlib 3.
import matplotlib.pyplot as plt
from matplotlib import transforms
def rainbow_text(x,y,ls,lc,**kw):
"""
Take a list of strings ``ls`` and colors ``lc`` and place them next to each
other, with text ls[i] being shown in color lc[i].
This example shows how to do both vertical and horizontal text, and will
pass all keyword arguments to plt.text, so you can set the font size,
family, etc.
"""
t = plt.gca().transData
fig = plt.gcf()
plt.show()
#horizontal version
for s,c in zip(ls,lc):
text = plt.text(x,y,s+" ",color=c, transform=t, **kw)
text.draw(fig.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, x=ex.width, units='dots')
#vertical version
for s,c in zip(ls,lc):
text = plt.text(x,y,s+" ",color=c, transform=t,
rotation=90,va='bottom',ha='center',**kw)
text.draw(fig.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, y=ex.height, units='dots')
plt.figure()
rainbow_text(0.05,0.05,"all unicorns poop rainbows ! ! !".split(),
['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'],
size=20)
Extending Yann's answer, LaTeX coloring now also works with PDF export:
import matplotlib
from matplotlib.backends.backend_pgf import FigureCanvasPgf
matplotlib.backend_bases.register_backend('pdf', FigureCanvasPgf)
import matplotlib.pyplot as plt
pgf_with_latex = {
"text.usetex": True, # use LaTeX to write all text
"pgf.rcfonts": False, # Ignore Matplotlibrc
"pgf.preamble": [
r'\usepackage{color}' # xcolor for colours
]
}
matplotlib.rcParams.update(pgf_with_latex)
plt.figure()
plt.ylabel(r'\textcolor{red}{Today} '+
r'\textcolor{green}{is} '+
r'\textcolor{blue}{cloudy.}')
plt.savefig("test.pdf")
Note that this python script sometimes fails with Undefined control sequence errors in the first attempt. Running it again is then successful.
After trying all the methods above, I return back to my stupid but easy method, using plt.text. The only problem is that you need to adjust the spaces between each word. You may need to adjust the positions several times, but I still like this way, because it
saves you from installing tex compilers,
does not require any special backends, and
free you from configuring matplotlib rc and configure back, or it may slows down your other plots, due to usetex=True
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
label_x = -0.15
ax.text(label_x, 0.35, r"Today", color='red', rotation='vertical', transform=ax.transAxes)
ax.text(label_x, 0.5, r"is", color='green', rotation='vertical', transform=ax.transAxes)
ax.text(label_x, 0.55, r"cloudy", color='blue', rotation='vertical', transform=ax.transAxes)
Is there a way in matplotlib to partially specify the color of a string?
Example:
plt.ylabel("Today is cloudy.")
How can I show "today" as red, "is" as green and "cloudy." as blue?
I only know how to do this non-interactively, and even then only with the 'PS' backend.
To do this, I would use Latex to format the text. Then I would include the 'color' package, and set your colors as you wish.
Here is an example of doing this:
import matplotlib
matplotlib.use('ps')
from matplotlib import rc
rc('text',usetex=True)
rc('text.latex', preamble='\usepackage{color}')
import matplotlib.pyplot as plt
plt.figure()
plt.ylabel(r'\textcolor{red}{Today} '+
r'\textcolor{green}{is} '+
r'\textcolor{blue}{cloudy.}')
plt.savefig('test.ps')
This results in (converted from ps to png using ImageMagick, so I could post it here):
Here's the interactive version. Edit: Fixed bug producing extra spaces in Matplotlib 3.
import matplotlib.pyplot as plt
from matplotlib import transforms
def rainbow_text(x,y,ls,lc,**kw):
"""
Take a list of strings ``ls`` and colors ``lc`` and place them next to each
other, with text ls[i] being shown in color lc[i].
This example shows how to do both vertical and horizontal text, and will
pass all keyword arguments to plt.text, so you can set the font size,
family, etc.
"""
t = plt.gca().transData
fig = plt.gcf()
plt.show()
#horizontal version
for s,c in zip(ls,lc):
text = plt.text(x,y,s+" ",color=c, transform=t, **kw)
text.draw(fig.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, x=ex.width, units='dots')
#vertical version
for s,c in zip(ls,lc):
text = plt.text(x,y,s+" ",color=c, transform=t,
rotation=90,va='bottom',ha='center',**kw)
text.draw(fig.canvas.get_renderer())
ex = text.get_window_extent()
t = transforms.offset_copy(text._transform, y=ex.height, units='dots')
plt.figure()
rainbow_text(0.05,0.05,"all unicorns poop rainbows ! ! !".split(),
['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'],
size=20)
Extending Yann's answer, LaTeX coloring now also works with PDF export:
import matplotlib
from matplotlib.backends.backend_pgf import FigureCanvasPgf
matplotlib.backend_bases.register_backend('pdf', FigureCanvasPgf)
import matplotlib.pyplot as plt
pgf_with_latex = {
"text.usetex": True, # use LaTeX to write all text
"pgf.rcfonts": False, # Ignore Matplotlibrc
"pgf.preamble": [
r'\usepackage{color}' # xcolor for colours
]
}
matplotlib.rcParams.update(pgf_with_latex)
plt.figure()
plt.ylabel(r'\textcolor{red}{Today} '+
r'\textcolor{green}{is} '+
r'\textcolor{blue}{cloudy.}')
plt.savefig("test.pdf")
Note that this python script sometimes fails with Undefined control sequence errors in the first attempt. Running it again is then successful.
After trying all the methods above, I return back to my stupid but easy method, using plt.text. The only problem is that you need to adjust the spaces between each word. You may need to adjust the positions several times, but I still like this way, because it
saves you from installing tex compilers,
does not require any special backends, and
free you from configuring matplotlib rc and configure back, or it may slows down your other plots, due to usetex=True
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
label_x = -0.15
ax.text(label_x, 0.35, r"Today", color='red', rotation='vertical', transform=ax.transAxes)
ax.text(label_x, 0.5, r"is", color='green', rotation='vertical', transform=ax.transAxes)
ax.text(label_x, 0.55, r"cloudy", color='blue', rotation='vertical', transform=ax.transAxes)