Category: Coding & Scripting

Free and Easy Horizontal Stacked Bar Graphs with Google Colab / Jupyter (as well as Python and Pandas)

Recently, my wife needed to create some stacked bar graphs for her publications. She wasn’t able to find a tool on the internet which allowed her to do this in an easy and shareable way. So I used this opportunity to look into Jupyter Notebooks. These notebooks allow you to document and run code and thus computations on a remote server from your browser and display the results directly in your browser as well

Google offers their version of Jupyter as a free service called Colab. Already having a Google account it was an easy choice to start there. But there are other similar services available on the internet and the code should run there as well (with minimal changes).

The file is quite self explanatory and can be saved within your own Google drive for editing. You can find it here.

The Notebook

Horizontal Stacked Bar Graph

This notebook will create a horizontal stacked bar graph within your browser based on the data you enter below. Just edit the data in 1) and execute the “gray blocks” in 1), 2) and 3) by either pressing “Shift + Enter” or the “Play” button in each blocks top left corner. To see the “Play” button you need to move your mouse over the block.

You can change the design of the graph in 2). This includes the unit which should be displayed on the bar. You will moste likely need to change the position of the legend as it depends on the data and size of the graph. To do this, change the x value of FigLegPosOffset slightly.

The code within a block is written in Python. Everything after a # is considered a comment. So you can add your own comments to your data or your changes.

1) Preparing the Data
Define your data below and execute the block when done. If everything is correct, your data will be displayed as table below the block.

# importing the functions needed to generate the graph 
import pandas as pd

# Axis captions
FigAxiXCap="Cocktails"
FigAxiYCap="Which Cocktail do you want for the Party?"

# Data (beware: the first entry will be displayed last)

## Data Indexes 
DataIndexes = ['Caipirinha', 'Pi√Īa Colada', 'Cuba Libre', 'Mai Tai', 'Ipanema', 'Caribbean Night']
## Data Options
DataYesPlease =  [73.2, 23.8, 24.7, 31.2, 56.6, 68.2]
DataSureWhyNot = [20.3, 51.7, 60.3, 19.2,  4.8, 22.3]
DataJustNo =     [ 6.5, 24.5,   15, 59.6, 38.6,  9.5]

## Name of each column (for the legend)
df = pd.DataFrame(data={'Yes, please!': DataYesPlease, 'Sure, why not?': DataSureWhyNot, 'Just no!': DataJustNo})

#-------------------------------------------------------------------------------
#no need to edit below this line
df.index = DataIndexes

# print table for convinience/debugging (transposed)
df

2) Figure Properties

In the block below you can change the format of the figure. When done (or fine with the defaults) execute it to set all properties.

#################
# Figure Design #
#################
#Figure
## Figure Size
FigSizeX = 20
FigSizeY = 5
## Figure background color 
FigBackColor="white"
## Axis Caption Size
FigAxiXCapFontSize=45
FigAxiYCapFontSize=45
## Axis Labels Font Size
FigLabelFontSize=20

# Blocks
## Colormap of the blocks (all Options: https://matplotlib.org/stable/gallery/color/colormap_reference.html)
FigColMap="Pastel2"
## Width of bars (everything above 1 will overlap)
FigBarsWidth=0.8
## FontSize of bar lables
FigBarsFontSize=20
# Data and Captions (needs to be defined)
## Unit to display within bars
FigBarUnit="%"

# Legend
## Legend Font Size
FigLegFontSize=20
## Legend position [best|upper right|upper left|lower left|lower right|right|center left|center right|lower center|upper center|center]
FigLegPos="upper center"
## Figure OffSet (will move the legend box slightly to match actual figure position)
FigLegPosOffset=(1.11, 1.0)

## 3) Generate and Display the Figure
The block below will generate the figure. You do not need to change anything here. Just be sure to execute it again **after changing anything above**.

# define plot type and layout
ax = df.plot(stacked=True, kind='barh', figsize=(FigSizeX, FigSizeY), colormap=FigColMap, width=FigBarsWidth, fontsize=FigBarsFontSize, xlim=[0,100])

# Add labels to the bars 
# Attribution: Trenton McKinney on StackExchange: https://stackoverflow.com/a/60895640/3764407 CC BY-SA 4.0
for rect in ax.patches:
    # Find where everything is located
    height = rect.get_height()
    width = rect.get_width()
    x = rect.get_x()
    y = rect.get_y()
    
    # The width of the bar is the data value and can be used as the label
    label_text = ""
    if width > 4:
      label_text = f'{width:.1f}' + FigBarUnit  ##### use {width:.2f} or {width:.3f} to increase precision  ####
    
    # ax.text(x, y, text)
    label_x = x + width / 2
    label_y = y + height / 2

    # plot only when height is greater than specified value
    if height > 0:
        ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=FigBarsFontSize)

# Set legend position and style
ax.legend(bbox_to_anchor=FigLegPosOffset, loc=FigLegPos, borderaxespad=0., fontsize=FigLegFontSize)    
# Set y label
ax.set_ylabel(FigAxiXCap, fontsize=FigAxiXCapFontSize)
# Set x label
ax.set_xlabel(FigAxiYCap, fontsize=FigAxiYCapFontSize)
# Set Background Color
ax.set_facecolor(FigBackColor)

3) Generate and Display the Figure
The block below will generate the figure. You do not need to change anything here. Just be sure to execute it again after changing anything above.

define plot type and layout
ax = df.plot(stacked=True, kind='barh', figsize=(FigSizeX, FigSizeY), colormap=FigColMap, width=FigBarsWidth, fontsize=FigBarsFontSize, xlim=[0,100])

# Add labels to the bars 
# Attribution: Trenton McKinney on StackExchange: https://stackoverflow.com/a/60895640/3764407 CC BY-SA 4.0
for rect in ax.patches:
    # Find where everything is located
    height = rect.get_height()
    width = rect.get_width()
    x = rect.get_x()
    y = rect.get_y()
    
    # The width of the bar is the data value and can be used as the label
    label_text = ""
    if width > 4:
      label_text = f'{width:.1f}' + FigBarUnit  ##### use {width:.2f} or {width:.3f} to increase precision  ####
    
    # ax.text(x, y, text)
    label_x = x + width / 2
    label_y = y + height / 2

    # plot only when height is greater than specified value
    if height > 0:
        ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=FigBarsFontSize)

# Set legend position and style
ax.legend(bbox_to_anchor=FigLegPosOffset, loc=FigLegPos, borderaxespad=0., fontsize=FigLegFontSize)    
# Set y label
ax.set_ylabel(FigAxiXCap, fontsize=FigAxiXCapFontSize)
# Set x label
ax.set_xlabel(FigAxiYCap, fontsize=FigAxiYCapFontSize)
# Set Background Color
ax.set_facecolor(FigBackColor)

4) Download the Figure (from Google Colab)
To export the generated figure use the following block. You can change the file name and type as well as its resolution (dots per inch/DPI).

# Define the file name
FigureFileName="figure"
# Define the type (jpg, png, pdf, svg)
FigureFileType="png"
# Define DPI (setting this too high might result in long wait times and finally an error)
FigureDPI=30

# do not edit below
FigFile=FigureFileName + "." + FigureFileType
ax.figure.savefig(FigFile,dpi=FigureDPI,bbox_inches = 'tight')
from google.colab import files
files.download("/content/" + FigFile )

Dart: code snippets for faster coding

There are different concepts that improve the data handling in Dart. The following list of snippets is a collection of the most handy once. Warning: this might simplify your code a lot! ūüėČ

The Spread Operator

Dart supports the spread operator, which allows to insert a collection (multiple elements) into a collection:

var values = [1, 2, 3];
var moreValues = [...values, 4, 5, 6];
print(moreValues);
// [1, 2, 3, 4, 5, 6]

Operators

Dart supports different operators. You can implement many of these operators as class members.

DescriptionOperator
unary postfixexpr++    expr--    ()    []    .    ?.
unary prefix-expr    !expr    ~expr    ++expr    --expr      await expr 
multiplicative*    /    %  ~/
additive+    -
shift<<    >>    >>>
bitwise AND&
bitwise XOR^
bitwise OR|
relational and type test>=    >    <=    <    as    is    is!
equality==    !=
logical AND&&
logical OR||
conditionalexpr1 ? expr2 : expr3
cascade..    ?..
assignment=    *=    /=   +=   -=   &=   ^=   etc.

Merging Two Maps

The code snippet below merges two maps. All (key) values in default are available in the merged map. This snippet only works for single level maps, multidimensional maps are only handled on the first level:

var options = {
    "hidden": "false",
    "option": "3",
};
var defaults = {
    "hidden": "true",
    "escape": "false",
};
options = {...defaults, ...options};
print(options);
// {hidden: false, escape: false, option: 3}

Default Value

To set a default value for a nullable variable, you can use the “if null” operator ‘??’:

const defaultValue = "Default";
final someVar = null;
var expectingValue = someVar ?? defaultValue;
print(expectingValue);
// "Default"

if and for in Collections

Dart offers an easy way to create dynamic lists by using conditionals (if) and repetition (for):

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];
var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');

String to Number

var stringValue = "123";
var intValue = int.parse(stringValue);
print(intValue);
// 123 (int)
var stringValue = "123.45";
var doubleValue = double.parse(stringValue);
print(doubleValue);
// 123.45 (double)

Numbers to String

Most of the Dart data types support a toString() method, which allows a conversion to strings. This is very handy for every parameter where a string is needed:

int intValue = 123;
var stringValue = intValue.toString();
print(stringValue);
// "123" (string)

Flutter: rounded corners for images

There are different possibilities to create a rounded corner of images:

ClipRRect

ClipRRect(
  borderRadius: BorderRadius.circular(10.0),
  child: Image.network(
    'https://example.com/image.jpg',
    height: 100.0,
    width: 100.0,
  ),
),

BoxDecoration

Container(
  height: 100.0,
  width: 100.0,
  decoration: BoxDecoration(
    image: DecorationImage(
      fit: BoxFit.cover, 
      image: NetworkImage('https://example.com/image.jpg'),
    ),
    borderRadius: BorderRadius.circular(10.0),
  ),
),

Photo by Chaitanya Tvs on Unsplash

brew: install Java on macOS

The following steps will guide you through the installation of Java on macOS.

First, check the available Java related formulas:

% brew search java

==> Formulae
app-engine-java              java                         javacc                       jslint4java                  pdftk-java
google-java-format           java11                       javarepl                     libreadline-java

Currently, there are two different version of Java: java and java11. To check the version of both, you can use the following commands:

% brew info java

openjdk: stable 16.0.1 (bottled) [keg-only]
Development kit for the Java programming language
https://openjdk.java.net/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/openjdk.rb
License: GPL-2.0-only with Classpath-exception-2.0
% brew info java11

openjdk@11: stable 11.0.10 (bottled) [keg-only]
Development kit for the Java programming language
https://openjdk.java.net/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/openjdk@11.rb
License: GPL-2.0-only

Depending on your requirements, you can install one of the above. For me, some of the libraries I use in Dart are currently not compatible with the latest Java version (16.0.1), so I decided to install Java 11 with LTS (long term support).

% brew install java11

This will install Java version 11.0.10 as listed in the output above. The output also shows the following hints:

For the system Java wrappers to find this JDK, symlink it with
  sudo ln -sfn /usr/local/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk

openjdk@11 is keg-only, which means it was not symlinked into /usr/local,
because this is an alternate version of another formula.

If you need to have openjdk@11 first in your PATH, run:
  echo 'export PATH="/usr/local/opt/openjdk@11/bin:$PATH"' >> ~/.zshrc

For compilers to find openjdk@11 you may need to set:
  export CPPFLAGS="-I/usr/local/opt/openjdk@11/include"

For me it was necessary to called the specified command, so that the system finds the java binary:

sudo ln -sfn /usr/local/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk

To see if Java was installed correctly, you can check the version of Java:

 % java --version

openjdk 11.0.10 2021-01-19
OpenJDK Runtime Environment (build 11.0.10+9)
OpenJDK 64-Bit Server VM (build 11.0.10+9, mixed mode)

That’s it!

Photo by Adam Wilson on Unsplash

Flutter: enable scroll-to-top for nested Scaffolds (e.g. in IndexedStack)

When using nested Scaffolds (e.g. in combination with IndexedStack), the PrimaryScrollController is not usable by default. An IndexedStack will load all subviews so scroll-to-top will change all scrollable views at the same time, even if they are not visible or it simply does not work, because the PrimaryScrollController can only be attached to a single Scaffold.

To overcome this issue, the scrolls_to_top package can be used. This also works for nested Scaffolds. The functionality of the package is described in this post (here is the english translation).

The following code example shows the usage. This is how to use ScrollsToTop within each children of IndexedStack:

  final _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: true,
      body: ScrollsToTop(
        onScrollsToTop: (event) {
          // onScrollsToTop will be called on each touch event, so check if the view is currently visible
          if (!widget.isOnScreen) return;

          _scrollController.animateTo(
            event.to,
            duration: event.duration,
            curve: event.curve,
          );
        },
        child: ListView.builder(
          itemBuilder: _itemBuilder,
          itemCount: 100,
          controller: _scrollController,
        ),
      ),
    );
  }

Flutter: add drag handle to ReorderableListView

By default, ReorderableListView only shows drag handles on desktop (GitHub). To enable a drag handle inside a ReorderableListView, it is possible to add the following code into the child’s subtree:

ReorderableDragStartListener(
  index: index,
  child: const Icon(Icons.drag_handle),
),

A full example usage with a very simple list:

var moviesTitles = ['Inception', 'Heat', 'Spider Man'];

ReorderableListView(
  onReorder: (oldIndex, newIndex) {
    // Handle reorder
  },
  children: moviesTitles.map((movie) {
    var index = moviesTitles.indexOf(movie);
    return ListTile(
      key: Key('${index}'),
      tileColor: Colors.white,
      title: Text(movie),
      trailing: ReorderableDragStartListener(
        index: index,
        child: const Icon(Icons.drag_handle),
      ),
      onTap: () {
        // Handle tap
      },
    );
  }).toList(),
);

This will result in the following output:

Adjust text color to be readable on light and dark backgrounds of user interfaces

Most modern user interfaces are supporting different color schemes for day and night: the so called light and dark modes. Selecting a text color for each of those modes is not a big deal and it’s the way to go when designing the user interface.

In some cases, the text color is driven by the displayed contents. In the example below, the tint color is matched to the color of the drink. The global tint color of this app is totally different, but this color adjustment gives a very nice effect. But as you might already see, there is a small problem when it comes to very light or very dark colors: each color either has a good readability on light or dark backgrounds. Some colors might fit to both, but that’s not always the case. In the example below, the light yellow is still visible, but when it comes to small icons or small text, the details are lost.

To overcome this issue, a simple solution is to select two colors for each recipe so that each mode has a different one. That’s fine, but it might totally change the effect of this colored pages.

Can we calculate a suitable color?

Some time ago, there was an article about Black or white text on a colour background? In this one, I described different algorithms to calculate the best text color (black or white) for a colored background. But now, we need the opposite: a colored text that has a good readability on white (light) or black (dark) backgrounds.

When we look at HSL and HSV/HSB color models, we already have a value for ‘lightness’ or ‘brightness’. The idea is to find a color that matches a given hue and saturation and that has a brightness which is readable on light and dark background. For this, we can use different algorithms. Very good results could be achieved with a ‘Weighted W3C Formula‘. This formula take into consideration that the human eye perceives some of the primary colors darker than others.

f'(x) = r ? 0.299 + g ? 0.587 + b ? 0.11

Each color that is located at the border between the black and white overlay is suitable for light and dark backgrounds.

Step 1: convert the given color to HSV/HSB

Step 2: keep hue and saturation constant and adjust the brightness (make the color lighter or darker)

Step 3: convert the HSV/HSB value back to the required color format

Implementation in PHP

A simple calculation for a given RGB color is shown below. The classes used in this snippet are available on GitHub. The code checks the initial brightness of the color and lightens or darkens the values until the ‘border’ calculated by the ‘Weighted W3C Formula’ is reached. This is the case for the value 127, the total range of the brightness is 0 to 255.

$hsv = Convert::rgb2hsv($rgb);

$step = 0.01;
$brightness = Calculate::weightedW3C($rgb);
if ($brightness < 127) {
    while ($brightness < 127 && $hsv[2] >= 0 && $hsv[2] <= 1) {
        $hsv[2] += $step;
        $brightness = Calculate::weightedW3C(Convert::hsv2rgb($hsv));
    }
} else {
    while ($brightness > 127 && $hsv[2] >= 0 && $hsv[2] <= 1) {
        $hsv[2] -= $step;
        $brightness = Calculate::weightedW3C(Convert::hsv2rgb($hsv));
    }
}

return Convert::hsv2rgb($hsv);

Some examples

But how does this result look for different colors? Let’s start with some dark colors. Those are fine for a light background, but they become unreadable on a dark one. The top colors show the input color (before) and the color below shows the output of the calculation above (after).

Color #632300 adjusted to be readable on light and dark background
Color #454545 adjusted to be readable on light and dark background

And now let’s look at some light colors which are fine for dark backgrounds, but they are totally unreadable on light backgrounds.

Color #73FEFF adjusted to be readable on light and dark background
Color #F0C&96 adjusted to be readable on light and dark background

The last color is similar to the example at the beginning and as you can see, the optimized color has a much better readability. This could be achieved for both light and dark colors. The code example shown above is written in PHP. An adoption should be easily possible for any other coding or scripting language

The algorithm mentioned in this post is also available on GitHub https://github.com/mixable/color-utils. This package is usable with composer:

composer require mixable/color-utils

The optimized color can be calculated with:

use Mixable\Color\Calculate;
// ...

$hex = '#ffcc00';
$optimizedColor = Calculate::readableColorForLightAndDarkBackground($hex);