More Complex Visualizations
Tutorial: Creating More Complex Visualizations with ScanPyImports
Here's an example showcasing the packages I frequently use in my projects. I'm sharing it to provide ideas on how to create more complex visualizations using ScanPyImports
, Matplotlib
and WordCloud
.
Feel free to customize these visualizations to suit your needs and apply the code to your own projects.
Importing Libraries¶
In [1]:
Copied!
from ScanPyImports.plotter import DataPlotter
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgb
from matplotlib.patches import FancyBboxPatch
import math
import random
import warnings
# Ignore warnings for clean output
warnings.filterwarnings("ignore")
from ScanPyImports.plotter import DataPlotter
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgb
from matplotlib.patches import FancyBboxPatch
import math
import random
import warnings
# Ignore warnings for clean output
warnings.filterwarnings("ignore")
Setting Up Constants and Utility Functions¶
Next, we define some constants and utility functions that will help us in creating the visualizations.
In [2]:
Copied!
# Constants for styling
ROUNDING = 0.1
BOXSTYLE_BG = f'round, pad=0.01, rounding_size={ROUNDING}'
# Add custom background to a plot (Axes)
def box_bg(ax, color):
box = FancyBboxPatch((0, 0), 1, 1, boxstyle=BOXSTYLE_BG,
ec='none', fc=color, clip_on=False, transform=ax.transAxes, zorder=0)
ax.add_patch(box)
# Define a color palette
def tab20() -> dict[str, tuple]:
"""Tableau 20 colors hex codes.
Returns:
Dictionary {color name: (dark, light)}
"""
colors = {
'blue': ('#1f77b4', '#aec7e8'),
'orange': ('#ff7f0e', '#ffbb78'),
'green': ('#2ca02c', '#98df8a'),
'red': ('#d62728', '#ff9896'),
'purple': ('#9467bd', '#c5b0d5'),
'brown': ('#8c564b', '#c49c94'),
'pink': ('#e377c2', '#f7b6d2'),
'gray': ('#7f7f7f', '#c7c7c7'),
'olive': ('#bcbd22', '#dbdb8d'),
'cyan': ('#17becf', '#9edae5')
}
return colors
# Generate the colors to be used for the words in the Cloud plot
def words_colors():
"""Colors in format `rgb(r, g, b)` as required by the WordCloud object."""
colors = tab20()
light_colors = [to_rgb(val[1]) for _, val in colors.items()]
light_colors += [(1.0,) * 3] * 10 # Fill with more whites
RGB = lambda x: math.floor(x * 255)
colorsRGB = [f'rgb{(RGB(a), RGB(b), RGB(c))}' for a, b, c in light_colors]
return colorsRGB
# Function to randomly choose a color for the Cloud plot
def color_func(*args, **kwargs):
"""Function to be passed as `**kargs` of the Cloud plot.
It follows the requirements of the WordCloud Api."""
colors = words_colors()
return random.choice(colors)
# Constants for styling
ROUNDING = 0.1
BOXSTYLE_BG = f'round, pad=0.01, rounding_size={ROUNDING}'
# Add custom background to a plot (Axes)
def box_bg(ax, color):
box = FancyBboxPatch((0, 0), 1, 1, boxstyle=BOXSTYLE_BG,
ec='none', fc=color, clip_on=False, transform=ax.transAxes, zorder=0)
ax.add_patch(box)
# Define a color palette
def tab20() -> dict[str, tuple]:
"""Tableau 20 colors hex codes.
Returns:
Dictionary {color name: (dark, light)}
"""
colors = {
'blue': ('#1f77b4', '#aec7e8'),
'orange': ('#ff7f0e', '#ffbb78'),
'green': ('#2ca02c', '#98df8a'),
'red': ('#d62728', '#ff9896'),
'purple': ('#9467bd', '#c5b0d5'),
'brown': ('#8c564b', '#c49c94'),
'pink': ('#e377c2', '#f7b6d2'),
'gray': ('#7f7f7f', '#c7c7c7'),
'olive': ('#bcbd22', '#dbdb8d'),
'cyan': ('#17becf', '#9edae5')
}
return colors
# Generate the colors to be used for the words in the Cloud plot
def words_colors():
"""Colors in format `rgb(r, g, b)` as required by the WordCloud object."""
colors = tab20()
light_colors = [to_rgb(val[1]) for _, val in colors.items()]
light_colors += [(1.0,) * 3] * 10 # Fill with more whites
RGB = lambda x: math.floor(x * 255)
colorsRGB = [f'rgb{(RGB(a), RGB(b), RGB(c))}' for a, b, c in light_colors]
return colorsRGB
# Function to randomly choose a color for the Cloud plot
def color_func(*args, **kwargs):
"""Function to be passed as `**kargs` of the Cloud plot.
It follows the requirements of the WordCloud Api."""
colors = words_colors()
return random.choice(colors)
Fancy Spiral Plot Function¶
We define a function fancy_spiral
to create a spiral plot with custom foreground and background colors.
In [3]:
Copied!
def fancy_spiral(plot, ax, fg_color, bg_color, title_text= None, fontname='Arial Rounded MT Bold'):
# Create a spiral plot
kwargs = dict(edgecolor=bg_color, facecolor=fg_color)
_, ax, _, texts = plot.spiral_frequencies(top=25,
label_padding=5,
ax=ax,
**kwargs)
# Adjust the properties of the bar labels
for text in texts:
text.set_fontsize('x-large')
text.set_color(fg_color)
text.set_font(fontname)
# Set the title properties
if title_text:
ax.set_title(title_text
, y = 0.9, x = 0.1
, ha = 'left', va = 'top'
, color = bg_color
, fontsize = 'x-large'
, fontweight = 'bold'
, fontname = fontname
, bbox = dict(facecolor=fg_color # Background color of the title box
, edgecolor='none'
, boxstyle='round,pad=1')
)
# Add a background round box to the plot
box_bg(ax, bg_color)
return ax, texts
def fancy_spiral(plot, ax, fg_color, bg_color, title_text= None, fontname='Arial Rounded MT Bold'):
# Create a spiral plot
kwargs = dict(edgecolor=bg_color, facecolor=fg_color)
_, ax, _, texts = plot.spiral_frequencies(top=25,
label_padding=5,
ax=ax,
**kwargs)
# Adjust the properties of the bar labels
for text in texts:
text.set_fontsize('x-large')
text.set_color(fg_color)
text.set_font(fontname)
# Set the title properties
if title_text:
ax.set_title(title_text
, y = 0.9, x = 0.1
, ha = 'left', va = 'top'
, color = bg_color
, fontsize = 'x-large'
, fontweight = 'bold'
, fontname = fontname
, bbox = dict(facecolor=fg_color # Background color of the title box
, edgecolor='none'
, boxstyle='round,pad=1')
)
# Add a background round box to the plot
box_bg(ax, bg_color)
return ax, texts
Fancy Cloud Plot Function¶
In [4]:
Copied!
def fancy_cloud(plot, ax, bg_color, fontname='Impact'):
# Add box background
box_bg(ax, bg_color) # Has to be added first!
# Set the font if provided
if fontname:
plot.settings.set_font(fontname)
# Define `**kwargs` parameters for the cloud plot
kwargs = dict(color_func=color_func, max_words=200)
# Define the `imshow` parameter.
# Here we ajust the size of the image to get some space
# between the image and axis
shrink = 0.10
imshow = dict(extent=(0 + shrink, 1 - shrink, 0 + shrink, 1 - shrink),
transform=ax.transAxes)
# Generate the cloud plot
_, ax, wc, _ = plot.cloud_frequencies(ax=ax, imshow=imshow, **kwargs)
return ax, wc
def fancy_cloud(plot, ax, bg_color, fontname='Impact'):
# Add box background
box_bg(ax, bg_color) # Has to be added first!
# Set the font if provided
if fontname:
plot.settings.set_font(fontname)
# Define `**kwargs` parameters for the cloud plot
kwargs = dict(color_func=color_func, max_words=200)
# Define the `imshow` parameter.
# Here we ajust the size of the image to get some space
# between the image and axis
shrink = 0.10
imshow = dict(extent=(0 + shrink, 1 - shrink, 0 + shrink, 1 - shrink),
transform=ax.transAxes)
# Generate the cloud plot
_, ax, wc, _ = plot.cloud_frequencies(ax=ax, imshow=imshow, **kwargs)
return ax, wc
Setting Up Projects and DataPlotters¶
We initialize a DataPlotter object per project (directories).
In [5]:
Copied!
# Define the projects to analyze
# These are my project directories, but you can provide your own directories and projects names!
projects = {
'@Graz' : DataPlotter(r"D:\Dropbox\Research\DigitalComp"),
'@WU' : DataPlotter(r'D:\Dropbox\Research\MigrationAT'),
'All' : DataPlotter(r'D:\Dropbox')
}
# Exclude specific packages by setting the `to_exclude` attribute
to_exclude = ['my_scripts', 'my_package', 'my_plots', 'my_module', 'my_sum']
for plotter in projects.values():
plotter.to_exclude = to_exclude
# Define the projects to analyze
# These are my project directories, but you can provide your own directories and projects names!
projects = {
'@Graz' : DataPlotter(r"D:\Dropbox\Research\DigitalComp"),
'@WU' : DataPlotter(r'D:\Dropbox\Research\MigrationAT'),
'All' : DataPlotter(r'D:\Dropbox')
}
# Exclude specific packages by setting the `to_exclude` attribute
to_exclude = ['my_scripts', 'my_package', 'my_plots', 'my_module', 'my_sum']
for plotter in projects.values():
plotter.to_exclude = to_exclude
Creating the Visualization¶
Finally, we create a mosaic plot with different subplots for each project.
In [6]:
Copied!
# Define subplot configuration
# Note: The projection for spiral plots must be 'polar'
per_subplot_kw = {key: {'projection': 'polar'} for key in projects.keys()}
# Set up the figure and axes
size = 4.5
fig, axs = plt.subplot_mosaic([['@WU', '@Graz'],
['cloud', 'All']],
per_subplot_kw=per_subplot_kw,
figsize=(size * 2, size * 2),
gridspec_kw={
"bottom": 0.1,
"top": 0.9,
"left": 0.1,
"right": 0.9,
"wspace": 0.1 * 2 / 3,
"hspace": 0.1 * 2 / 3
}
)
# Define colors
colors = tab20()
# Create cloud plot for the project 'All'
bg_color = colors['blue'][0]
axs['cloud'], wc= fancy_cloud(projects['All'], ax=axs['cloud'],bg_color=bg_color)
# Create spiral plots for each project
fg_color = 'white'
bg_colors = ['gray', 'orange', 'red']
for (project, plot), color in zip(projects.items(),bg_colors):
bg_color = colors[color][0]
axs[project], texts = fancy_spiral(plot, axs[project]
, fg_color, bg_color
, title_text=project)
# Manually adjust labels that go out of the plot (`All` spiral)
if project == 'All':
for text in texts[-4:]:
text.set_horizontalalignment('right')
x,y = text.get_position()
text.set_position((x,y-10))
text.set_color(bg_color)
# Define subplot configuration
# Note: The projection for spiral plots must be 'polar'
per_subplot_kw = {key: {'projection': 'polar'} for key in projects.keys()}
# Set up the figure and axes
size = 4.5
fig, axs = plt.subplot_mosaic([['@WU', '@Graz'],
['cloud', 'All']],
per_subplot_kw=per_subplot_kw,
figsize=(size * 2, size * 2),
gridspec_kw={
"bottom": 0.1,
"top": 0.9,
"left": 0.1,
"right": 0.9,
"wspace": 0.1 * 2 / 3,
"hspace": 0.1 * 2 / 3
}
)
# Define colors
colors = tab20()
# Create cloud plot for the project 'All'
bg_color = colors['blue'][0]
axs['cloud'], wc= fancy_cloud(projects['All'], ax=axs['cloud'],bg_color=bg_color)
# Create spiral plots for each project
fg_color = 'white'
bg_colors = ['gray', 'orange', 'red']
for (project, plot), color in zip(projects.items(),bg_colors):
bg_color = colors[color][0]
axs[project], texts = fancy_spiral(plot, axs[project]
, fg_color, bg_color
, title_text=project)
# Manually adjust labels that go out of the plot (`All` spiral)
if project == 'All':
for text in texts[-4:]:
text.set_horizontalalignment('right')
x,y = text.get_position()
text.set_position((x,y-10))
text.set_color(bg_color)