Difference between Scatter object and figure.scatter method - python-3.x

I have the following question related to these two graphs:
Graph 1:
output_notebook()
scatter = Scatter(df_b, x='log_umsatz', y='log_fte', color='target', legend="top_right")
show(scatter)
Graph 2
output_notebook()
scatter = figure(plot_width=500, plot_height=500)
scatter.scatter(x=df_b['log_umsatz'], y=df_b['log_fte'], color=df['target'])
p.legend.location = "top_left"
p.legend.click_policy="hide"
show(scatter)
As you can see, I generated two scatter graphs using bokeh. In the second graph, I try to introduce some interactivity with p.legend.click_policy="hide". I have two issues: The interactivity doesn't work, and legend and color coding are lost in the second example. How come? I expected graph 1 and graph 2 to be identical.

Your main issue is that you are using for Graph 1 Scatter which is a Bokeh Charts model. Bokeh Charts is a high level library for plotting data and does a lot of data processing and chart formatting for you behind the scenes. In Graph 2, you are using a Bokeh glyph to create your plot and so you need to be much more explicit in what you want it to do.
Fixing up your code I can produce the same graph as that original Scatter.
cds = ColumnDataSource(df_b)
color_mapper = CategoricalColorMapper(
palette=['red', 'green'], factors=[0, 1])
scatter = figure(plot_width=500, plot_height=500)
scatter.circle(x='log_umsatz', y='log_fte',
color={'field': 'target', 'transform': color_mapper}, alpha=0.5,
source=cds, legend='target')
scatter.legend.location = "top_right"
As you can see, we need to call in multiple other Bokeh objects. ColumnDataSource to store the pandas data and CategoricalColorMapper to map the colors to the factors.
Now adding an interactive legend to the plot is a little more complicated. Right now on Bokeh interactive legend works on a per glyph basis. That is to say each glyph must be plotted separately to be intractable. You can read more about it here, and here's a quick demo to help you.
scatter = figure(plot_width=500, plot_height=500)
scatter.circle(x=[1, 2, 3], y=[1, 2, 3], color='red', legend='0', alpha=0.5)
scatter.circle(x=[4, 5], y=[4, 5], color='green', legend='1', alpha=0.5)
scatter.legend.location = "top_right"
scatter.legend.click_policy = "hide"

Related

Is there a way to apply 3d-like appearance (like bevel) to 2d matplotlib plots?

I've been working for a while with the matplotlib package in Python, and I know that you can do 2D graphs (usually involving two "dimensions", x and y) or 3D graphs (with functions like plot3D). However, I am unable to find documentation about giving a '3D aesthetic' to a 2D plot.
That is, giving the plot a bit of volume, some shadows, etc.
To give an example, let's say I wanted to create a donut chart in matplotlib. A first draft could be something like this:
import matplotlib.pyplot as plt
#Given an array of values 'values' and,
#optionally, an array of colors 'colors'
#and an array of labels 'labels':
ax = plt.subplot()
ax.pie(
x = values,
labels = labels,
colors = colors
)
center_circle = plt.Circle((0,0), radius = 0.5, fc = "white")
ax.add_artist(center_circle)
plt.show()
However, a quick graph with Excel can give a much more appealing result:
Looking at the documentation of plt.pie, I was not able to find anything significant, apart from the parameter shadow, which when set to True, gives an underwhelming result:
Also, I would like to add effect such as the use of bevel (like the 3d-look of the borders of each wedge of the pie) and more style things. How could I improve the look of my graph with matplotlib? Is it even possible to accomplish it with this library?
One solution might be using a different library. I am not familiar with seaborn, but I know it is also a powerful visualisation library. The same with plotly. Does any one of these libraries allow for these kind of customisations?
There are a whole bunch of options on the matplotlib website for pie charts here: https://matplotlib.org/stable/gallery/pie_and_polar_charts/index.html
Matplotlib does not have a built-in option to add a bevel to a 2D pie chart or any other types of charts directly.
But, you could do this (raised shaddow) for a 3d effect:
import matplotlib.pyplot as plt
# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, startangle=90)
ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.show()
which give this:

Plotly candlestick cannot be drawn on top of scatter

I'm trying to draw a candlestick plot and an scatter line below it, but the candlestick chart is always drawn first/at the bottom:
data_sep = []
# Price
data_sep.append(
go.Scatter(
x=data['Index'],
y=data['Values'],
mode='lines',
line=dict(color='darkgray', width=1),
name='Price'
)
)
data_sep.append(
go.Candlestick(
x=data['Index'],
open=data['Values'].shift(1),
high=data['Values_HIGH'],
low=data['Values_LOW'],
close=data['Values'],
name='Candlestick',))
fig = go.Figure(data=data_sep)
fig.show()
Changing the order of the code blocks do not change the ending result.
Is it possible to do this this way?
I'm using an array to save figures because later on I need to add several other scatters, all of them to be shown below the candlestick.

Matplotlib not displaying all values

I am trying to display the following values in the form of a bar chart. However, I am only getting one value displayed (619,1). Below is the code which I used in an attempt to plot the below graph:
import matplotlib.pyplot as plt
plt.style.use('ggplot')
values= [1, 2, 3, 4, 5]
a = [619, 101, 815, 1361, 178]
plt.figure(figsize=(5, 5))
plt.bar(a, values)
plt.show()
The bar width is set to a default value of 0.8 so when your x-axis has such a large range, the bars are so skinny that they disappear.
The reason for the 0.8 is that bar charts are typically used for labelled categories, which are effectively spaced by 1 along the x-axis.
So you can set the width directly. (It's also possible to calculate a width, to make this more automatic, but then you need to decide about overlaps, etc.)
plt.figure(figsize=(5, 5))
plt.xlim(0, 1450)
plt.bar(a, values, width = 50)
It seems your data might be better suited for a horizontal bar plot (but don't take this too seriously as it may not have the right meaning at all), and if you want horizontal bars, you can do so like this:
plt.barh(values, a)

Using "hue" for a Seaborn visual: how to get legend in one graph?

I created a scatter plot in seaborn using seaborn.relplot, but am having trouble putting the legend all in one graph.
When I do this simple way, everything works fine:
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
df2 = df[df.ln_amt_000s < 700]
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)
The result is a scatter plot as desired, with the legend on the right hand side.
However, when I try to generate a matplotlib figure and axes objects ahead of time to specify the figure dimensions I run into problems:
a4_dims = (10, 10) # generating a matplotlib figure and axes objects ahead of time to specify figure dimensions
df2 = df[df.ln_amt_000s < 700]
fig, ax = plt.subplots(figsize = a4_dims)
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)
The result is two graphs -- one that has the scatter plots as expected but missing the legend, and another one below it that is all blank except for the legend on the right hand side.
How do I fix this such? My desired result is one graph where I can specify the figure dimensions and have the legend at the bottom in two rows, below the x-axis (if that is too difficult, or not supported, then the default legend position to the right on the same graph would work too)? I know the problem lies with "ax=ax", and in the way I am specifying the dimensions as matplotlib figure, but I'd like to know specifically why this causes a problem so I can learn from this.
Thank you for your time.
The issue is that sns.relplot is a "Figure-level interface for drawing relational plots onto a FacetGrid" (see the API page). With a simple sns.scatterplot (the default type of plot used by sns.relplot), your code works (changed to use reproducible data):
df = pd.read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv", index_col=0)
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width',
hue = 'Species', legend = 'brief',
ax=ax, data = df)
plt.show()
Further edits to legend
Seaborn's legends are a bit finicky. Some tweaks you may want to employ:
Remove the default seaborn title, which is actually a legend entry, by getting and slicing the handles and labels
Set a new title that is actually a title
Move the location and make use of bbox_to_anchor to move outside the plot area (note that the bbox parameters need some tweaking depending on your plot size)
Specify the number of columns
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width',
hue = 'Species', legend = 'brief',
ax=ax, data = df)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], loc=8,
ncol=2, bbox_to_anchor=[0.5,-.3,0,0])
plt.show()

Creating 3 pandas series as pie charts in one plot

I'm setting up a figure to display 3 pie charts. Data for the charts come from 3 separate pandas series. I suppose I could merge the series into a df and create subplots via that df but I doubt it's needed.
My current code generates 3 pie charts. But they all overlap. I'm confused about how to arrange them.
S19E_sj = (BDdf.loc[BDdf['GRPCODE'] == 'SJ3219'])['Result'].value_counts()
S19E_ge = (BDdf.loc[BDdf['GRPCODE'] == 'G1932'])['Result'].value_counts()
S19E_jl = (BDdf.loc[BDdf['GRPCODE'] == 'JLG1930'])['Result'].value_counts()
fig, ax = plt.subplots(figsize = (8,6))
S19E_sj.plot.pie()
S19E_ge.plot.pie()
S19E_jl.plot.pie()
Although you failed to provide a Minimal, Complete, and Verifiable example, you can try something like this. Create a figure containing 3 subplots arranged in a row, and then assign them individually to your three pie chart commands
fig, axes = plt.subplots(1, 3, figsize = (8,6))
S19E_sj.plot.pie(ax=axes[0])
S19E_ge.plot.pie(ax=axes[1])
S19E_jl.plot.pie(ax=axes[2])
plt.tight_layout()
plt.show()

Resources