Adding Labels to Markers on Relplot - matplotlib

I am a bit lost on the best approach to add labels to my markers with a seaborn relplot. I see in the matplotlib documentation that there is a axes.text() method that looks to be the right approach, but it doesn't appear that this method exists. Does seaborn behave differently than matplotlib in this sense? What would the right approach be?
Error:
AttributeError: 'numpy.ndarray' object has no attribute 'text'
Code:
line_minutes_asleep = sns.relplot(
x = "sleep_date",
y = "minutes_asleep",
kind = "line",
data = df,
height=10, # make the plot 5 units high
aspect=3
)
x = df.sleep_date
y = df.minutes_asleep
names = df.minutes_asleep
print(line_minutes_asleep.axes.text())

relplot returns a FacetGrid, which is a figure containing several subplots. The property .axes of a FacetGrid is a 2D ndarray of Axes objects. Therefore, you need to use FacetGrid.axes[i,j] to get a reference to the subplot.
If you want to write something in the first subplot (axes[0,0]), at the position x,y=(20,5), you would need to do:
import seaborn as sns
sns.set(style="ticks")
tips = sns.load_dataset("tips")
g = sns.relplot(x="total_bill", y="tip", hue="day", data=tips)
g.axes[0,0].text(20,5,"this is a text")

Related

Seaborn lineplot, different markers for different boolean values

I have a dataframe that consists of 3 columns. Champion (categorical, holds string values), total damage done (numerical), win (holds Boolean values, True or False). I want to draw a line and I want its markers to be "o" if "win == True" and "x" if "win == False". I tried the code that I have attached here but it doesn't work.It gives ValueError: Filled and line art markers cannot be mixed.I tried to do it with hue or style but it changes the line style rather than marker. And I tried giving style my win column and I tried to make markers to follow from that, but that didn't work either. Can anyone help?
Thanks
Only with style ScreenShot
fig = plt.figure(figsize=(12,8))
h = sns.lineplot(data=skyhill_all,x='champion',y='totalDamageDealt',style='win',markers=['o','x'])
h.yaxis.set_minor_locator(AutoMinorLocator())
h.tick_params(which='both',width=2)
h.tick_params(which='major',length=8)
h.tick_params(which='minor',length=4)
h.set_ylabel('Total Damage Done')
h.set_xlabel('Played Champions')
h.set_yticks(np.arange(5000,75000,5000))
print(h)
The simplest approach is to draw a line graph in matplotlib, then set the markers and colors in a scatter plot. The rest is setting the seaborn style. You can choose your own style in the future.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
skyhill_all = pd.DataFrame({'champion':list('ABCDEF'),
'totalDamageDealt':np.random.randint(100,10000,6),
'win': [True,False,False,True,True,False]})
plt.style.use('seaborn-white')
fig, ax = plt.subplots()
m = ['o' if x == 1 else 'x' for x in skyhill_all['win']]
c = ['orange' if x == 1 else 'blue' for x in skyhill_all['win']]
ax.plot(skyhill_all['champion'], skyhill_all['totalDamageDealt'])
for i in range(len(skyhill_all)):
ax.scatter(skyhill_all['champion'][i], skyhill_all['totalDamageDealt'][i], marker=m[i], color=c[i])
plt.show()

Matplotlib: How to plot line plots for multiple years with month and day on x axis?

I have a datarame similar to the following:
using the following code, I am able to plot a chart:
fig , ax = plt.subplots(nrows=1, ncols=1,figsize=(15,8))
# colors = {1:'red', 2:'green', 3:'blue', 4: 'yellow', 5:'aqua', 6:'salmon', 7:'plum', 8:'khaki', 9:'sienna', 10:'yellowgreen',
# 11:'cyan', 12: 'gold'}
months = euro_to_dollar["month"].unique()
for m in months:
ax.plot(euro_to_dollar[(euro_to_dollar["Time"].dt.strftime('%Y') == '2020') & (euro_to_dollar["month"]== m)]["dayofmonth"],
euro_to_dollar[(euro_to_dollar["Time"].dt.strftime('%Y') == '2020') & (euro_to_dollar["month"]== m)]["US_dollar"],
alpha = 0.5, label = m)
#color = euro_to_dollar[(euro_to_dollar["Time"].dt.strftime('%Y') == '2020') & (euro_to_dollar["month"]== m)]["month"].map(colors))
ax.grid(b=False)
ax.set_xticks(np.arange(1,len(euro_to_dollar["dayofmonth"].unique())))
ax.set_title("Month vs euro_dollar_rate Mean")
ax.legend(loc='best')
plt.show()
My questions:
I tried to manually type in the colors and try to use map function as below:
color = euro_to_dollar[(euro_to_dollar["Time"].dt.strftime('%Y') == '2020') & (euro_to_dollar["month"]== m)]["month"].map(colors)
but failed with error: ValueError: Invalid RGBA argument: 5375 red. Whats this error and how to handle this?
How to I customize the colors for each category dynamically? I don't want to manually type in the colors as below:
colors = {1:'red', 2:'green', 3:'blue', 4: 'yellow', 5:'aqua', 6:'salmon', 7:'plum', 8:'khaki', 9:'sienna', 10:'yellowgreen',
11:'cyan', 12: 'gold'}
I have seen in some plots where others were using different matplotlib colors by importing cm library from matplotlib. My question is how to assign different colors for N categories dynamically such that each category can be represented by its own color. I have seen others using numpy functions like this:
viridis = cm.get_cmap('viridis', 256)
newcolors = viridis(np.linspace(0, 1, 256))
pink = np.array([248/256, 24/256, 148/256, 1])
Thanks!
Solutions to questions
1. ValueError
The error seems to be caused by the fact that color is a pandas series object instead of a string or other valid color object. You can solve this by getting the appropriate string directly from the colors dictionary like this: color = colors[m].
2. Extracting colors from a colormap
The first section of the matplotlib tutorial Creating Colormaps in Matplotlib shows how to extract colors from the colormaps. As it is explained, there are two types of colormap objects in matplotlib (ListedColormap and LinearSegmentedColormap) which have partially different methods to extract the colors. Note that the documentation page displaying the built-in colormaps does not show what type of colormap object is used for each colormap. You get that information when calling the colormap:
plt.get_cmap('viridis')
# <matplotlib.colors.ListedColormap at 0x16b5859b790>
To get an overview table of all colormaps and their object type, you can run this (see also this question):
for cmap in plt.colormaps():
print(f'{cmap:<20} {str(plt.get_cmap(cmap)).split(".")[-1].split()[0]}')
To answer your question, here is a method to extract a list of colors of the number you want from both types of colormaps consistently which you can then access using the month number minus 1 as index:
months = euro_to_dollar['month'].unique()
cmap = plt.get_cmap('any_colormap_name')
colors = cmap(np.linspace(0, 1, len(months)))
for m in months:
ax.plot(...,
color = colors[m-1])
Suggestion: use seaborn lineplot function
The code can be simplified by using the lineplot function of the seaborn package. You can choose any matplotlib colormap with palette and activate coloring according to the months with hue='month'. Here is a complete example.
Create sample dataset
import numpy as np # v 1.20.2
import pandas as pd # v 1.2.5
import matplotlib.pyplot as plt # v 3.3.4
import seaborn as sns # v 0.11.1
rng = np.random.default_rng(seed=123) # random number generator
bdate = pd.bdate_range(start='2019-01-01', end='2021-07-24')
daily_value_change = rng.normal(loc=0, scale=0.005, size=bdate.size)
value = 1.1 + np.cumsum(daily_value_change)
euro_to_dollar = pd.DataFrame(dict(Time=bdate, US_dollar=value))
euro_to_dollar['year'] = euro_to_dollar['Time'].dt.year
euro_to_dollar['month'] = euro_to_dollar['Time'].dt.month
euro_to_dollar['dayofmonth'] = euro_to_dollar['Time'].dt.day
euro_to_dollar.head()
Select year and plot data with seaborn
euro_to_dollar_2020 = euro_to_dollar[euro_to_dollar['year'] == 2020]
ax = sns.lineplot(data=euro_to_dollar_2020, x='dayofmonth', y='US_dollar',
hue='month', palette='viridis')
ax.figure.set_size_inches(10, 6)
ax.set_xticks(euro_to_dollar['dayofmonth'].unique())
ax.set_title('Month vs euro_dollar_rate Mean')
ax.legend(ax.lines, euro_to_dollar['month'].unique(), title='month')
plt.show()

Scatter plot with variable marker size (seaborn)

I am using a seaborn pairplot to plot a scatter plot of different dimensions of my datapoints. However, I want the markers of the datapoints to have a size that corresponds to one of the dimensions of the datapoints. I have the following code:
markersize = 1000* my_dataframe['dim_size'] / sum(my_dataframe['dim_size'])
sns.set_context("notebook", font_scale=1.5, rc={'figure.figsize': [11, 8]})
sns.set_style("darkgrid", {"axes.facecolor": ".9"})
kws = dict(s=markersize, linewidth=.5, edgecolor="w")
sbax = sns.pairplot(my_dataframe, hue='dim_hue' x_vars=['dim_1', 'dim_2'], y_vars=['dim_3', 'dim_4'], size=5, plot_kws=kws)
axes = sbax.axes
for a in axes.flatten():
a.set_ylim([0,1])
a.set_xlim([0,1])
If I do print(kws), I see in the dictionary that the sizes are all different and vary from 40 to 2000. However, the markers on the plot are all the same. Is there any way to achieve what I want?
Btw, this works very well with lmplot if I set the parameter scatter_kws={"s": markersize}.
Thanks!
import seaborn as sns
import matplotlib.pyplot as plt
iris = sns.load_dataset("iris")
size = 100 * (iris.petal_length / iris.petal_length.max())
g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"], size=5)
g.map(plt.scatter, s=size)

PyPlot, PyCall Legend Font Size from Julia

When I try and change the legend fontsize using PyPlot from julia, I get an error message saying "Pyerror..got an unexpected key word "'fontsize'". This happens when I try both of the standard formulations shown below:
ax[:legend]( ("Data", "Model Predictions"),fontsize=4,loc=4 )
ax[:legend]( ("Data", "Model Predictions"),prop={fontsize: "small"},loc=4 )
Note that changing fontsize works fine with other objects e.g. xlabel
Any ideas?
Does this work for you?
using PyPlot
fig, ax = PyPlot.subplots()
ax[:plot](rand(10), rand(10), label = "Data")
ax[:legend](loc="best", fontsize=4)
If not, what versions of Julia, PyPlot, PyCall, and Python are you on?
The help for legend states that:
prop : None or :class:matplotlib.font_manager.FontProperties or dict
The font properties of the legend. If None (default), the current
:data:matplotlib.rcParams will be used.
So the prob keyword argument expects a dict with font properties. Dicts is constructed in julia as [key => val]. This dict can then contain the properties. The property you would like to set is size and not fontsize because the prob keyword argument only contains font properties.
ax[:legend](("Data", "Model Predictions"), prop=["size" => "small"], loc=4)
In the end the above two suggestions didn't work (I think it's a version issue). But this did:
using PyPlot
#pyimport matplotlib.pyplot as plt
#pyimport matplotlib.font_manager as fm
prop = fm.FontProperties(size=9)
fig, ax = PyPlot.subplots()
ax[:plot](rand(10), rand(10), label = "Data")
ax[:legend](loc="best", prop=prop)

Matplotlib histogram with errorbars

I have created a histogram with matplotlib using the pyplot.hist() function. I would like to add a Poison error square root of bin height (sqrt(binheight)) to the bars. How can I do this?
The return tuple of .hist() includes return[2] -> a list of 1 Patch objects. I could only find out that it is possible to add errors to bars created via pyplot.bar().
Indeed you need to use bar. You can use to output of hist and plot it as a bar:
import numpy as np
import pylab as plt
data = np.array(np.random.rand(1000))
y,binEdges = np.histogram(data,bins=10)
bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
menStd = np.sqrt(y)
width = 0.05
plt.bar(bincenters, y, width=width, color='r', yerr=menStd)
plt.show()
Alternative Solution
You can also use a combination of pyplot.errorbar() and drawstyle keyword argument. The code below creates a plot of the histogram using a stepped line plot. There is a marker in the center of each bin and each bin has the requisite Poisson errorbar.
import numpy
import pyplot
x = numpy.random.rand(1000)
y, bin_edges = numpy.histogram(x, bins=10)
bin_centers = 0.5*(bin_edges[1:] + bin_edges[:-1])
pyplot.errorbar(
bin_centers,
y,
yerr = y**0.5,
marker = '.',
drawstyle = 'steps-mid-'
)
pyplot.show()
My personal opinion
When plotting the results of multiple histograms on the the same figure, line plots are easier to distinguish. In addition, they look nicer when plotting with a yscale='log'.