Lollipop & Dumbbell Charts with Plotly | by Darío Weitz | Towards Data Science

A lollipop chart (LC) is a handy variation of a bar chart where the bar is replaced with a line and a dot at the end. They are two-dimensional with two axes: one axis shows categories or a time series, the other axis shows numerical values. Those numerical values are indicated by the position of the dot at the end of the line. A vertically oriented LC displays the categorical variable on the y-axis whilst a horizontally oriented LC displays it on the x-axis.
Just like bar graphs, lollipop plots are used to make comparisons between different items or categories. They are also used for ranking or for showing trends over time. We compare only one numerical variable per item or category. They are not suitable for relationships, distribution, or composition analysis.
LCs are preferred to bar charts when you have to display a large number of similar high values. In that case with a standard bar plot, you may get a cluttered chart and experience an optical effect called a Moiré pattern. Although they are equivalent to bar charts, this equivalence is only valid for standard bar charts; do not try to extend it to Stacked, Clustered, or Overlapping Bar Graphs.
On the other hand, Dumbbell charts (DC) are an alternative to clustered bar charts and slope charts. Also known as DNA graphs, they indicate the change between two data points. Each point corresponds to a numerical value that is compared in the same dimension in a very direct way.
They are very efficient when comparing differences, ranges, spread, or distance between points. They are used to make comparisons between categories or to observe a trend between two-time intervals.
Figure 0 shows a schematic vertically oriented Dumbbell chart.
Professional data scientists should be familiar with different statistical measures used to describe the observations of their studies or projects. Common measures of central tendency with which they should be quite familiar are the mean, median, and mode.
The mean of a dataset is found by adding all numbers in the dataset and then dividing by the number of values in it. The median is the middle value when the dataset is sorted from least to greatest. Mode is a measure of central tendency representing the value that occurs most frequently in the dataset.
As there are a lot of external factors that limit the use of mean, the median is widely used, particularly in skewed distributions. The median is considered robust against outliers, while the mean is considered sensitive to outliers. Remember that an outlier (anomalous value) is a piece of data that is very different from all the others in the dataset and does not seem to fit the same pattern. The median generally gives a more appropriate idea of the data distribution.
Plotly Express, an object-oriented interface to figure creation, was released in 2019. It is a high-level wrapper for Plotly.py that includes functions to plot standard 2D & 3D charts and choropleth maps. Fully compatible with the rest of the Plotly ecosystem, is an excellent tool for the rapid development of exploratory charts.
Plotly provides a group of classes called graph objects that may be used to construct figures. The plotly.graph_objects module contains a hierarchy of Python classes. Figure is a primary class. Figure has a data attribute and a layout attribute. The data attribute has more than 40 objects, each one refers to a specific type of chart (trace) with its corresponding parameters. The layout attribute specifies the properties of the figure as a whole (axes, title, shapes, legends, etc.).
We worked with a public dataset downloaded from the community GitHub [1]. The dataset contains records related to world wealth and income. We want to know which measurement of central tendency is the most appropriate for analyzing the dataset.
First, we imported Plotly Express as px, the module plotly.graph_objects as go, the Pandas library as pd and converted our csv file into a dataframe:
import pandas as pdimport plotly.express as pximport plotly.graph_objects as godf_Ww = pd.read_csv(path + 'WorldWealth.csv',                     index_col = False,                    header = 0, sep = ';', engine='python')
The way the data is stored in the file is not the way we need for the drawing. As every column is stored as an object type, we need to convert the columns Median_Wealth and Mean_Wealth to integer:
df_Ww['Mean_Wealth']   = df_Ww['Mean_Wealth'].str.replace(                               ',', '').astype(str).astype(int)df_Ww['Median_Wealth'] = df_Ww['Median_Wealth'].str.replace(                               ',', '').astype(str).astype(int)types = df_Ww.dtypes
The screenshot below shows the first ten records of the transformed dataframe:
After these three lines of code, we are ready to draw lollipop charts.
We used .add_trace(go.Scatter( to draw the points and .add_shape(type=’line’ to draw the corresponding lines. We selected to draw a horizontally oriented LC by displaying the categorical variable [“Country”] on the y-axis. We used mode = ‘markers’ to choose a dot marker.
Standalone lines are added to the figure using fig1.add_shape(type='line'. Note that we used the counter variable of the for loop to set y0 and y1 values.
We updated the chart with update.layout: set the title and the size of the font. We set the template= ‘seaborn+ygridoff’: in this case, the resulting template is computed by merging the collection of two registered templates (seaborn & ygridoff). We defined the figure dimensions with width and height. Then we updated the x-axis (title and range). We saved the chart as a static png file and, finally, we drew the chart.
First we drew a ranking of the top 15 countries according to their median wealth:
df_WwT = df_Ww.sort_values(by = ['Median_Wealth'],                    ascending = False).iloc[0:15].reset_index()fig1 = go.Figure()# Draw pointsfig1.add_trace(go.Scatter(x = df_WwT["Median_Wealth"],                           y = df_WwT["Country"],                          mode = 'markers',                          marker_color ='darkblue',                          marker_size  = 10))# Draw linesfor i in range(0, len(df_WwT)):               fig1.add_shape(type='line',                              x0 = 0, y0 = i,                              x1 = df_WwT["Median_Wealth"][i],                              y1 = i,                              line=dict(color='crimson', width = 3))# Set titlefig1.update_layout(title_text =                    "Median Wealth (U$S) Top 15 Countries",                   title_font_size = 30)# Set x-axes rangefig1.update_xaxes(title = 'Median Wealth' , range=[0, 250000])fig1.write_image(path + "figlollipop1.png")fig1.show()
Then we drew a ranking of the bottom 15 countries according to their mean wealth:
df_WwB = df_Ww.sort_values(by =                    ['Mean_Wealth']).iloc[0:15].reset_index()fig2 = go.Figure()# Draw Pointsfig2.add_trace(go.Scatter(x = df_WwB["Mean_Wealth"],                           y = df_WwB["Country"],                          mode = 'markers',                          marker_color ='darkorange',                          marker_size  = 10)# Draw Linesfor i in range(0, len(df_WwB)):               fig2.add_shape(type='line',                              x0 = 0, y0 = i,                              x1 = df_WwB["Mean_Wealth"][i],                              y1 = i,                              line=dict(color='crimson', width = 3))# Set titlefig2.update_layout(title_text =                   "Mean Wealth (U$S) Bottom 15 Countries",                    title_font_size = 30)# Set x-axes rangefig2.update_xaxes(title = 'Mean Wealth', range=[0, 2000])fig2.write_image(path + "figlollipop2.png")fig2.show()
Then we designed two horizontally oriented Dumbbell Charts to compare Mean Wealth against Median Wealth for the 15 Top countries and the 15 Bottom countries. Now we need two .add_trace(go.Scatter(: one for Mean Wealth dots and another for Median Wealth dots. Regarding to .add_shape(type=’line’ we used: df_WwT[“Median_Wealth”][i] for x0 and df_WwT[“Mean_Wealth”][i] for x1.
fig3= go.Figure()fig3.add_trace(go.Scatter(x = df_WwT["Median_Wealth"],                           y = df_WwT["Country"],                          mode = 'markers',                          marker_color = 'darkblue',                          marker_size = 10,                          name = 'Median'))fig3.add_trace(go.Scatter(x = df_WwT["Mean_Wealth"],                           y = df_WwT["Country"],                          mode = 'markers',                          marker_color = 'darkorange',                           marker_size = 10,                          name = 'Mean'))for i in range(0, len(df_WwT)):               fig3.add_shape(type='line',                              x0 = df_WwT["Median_Wealth"][i],                              y0 = i,                              x1 = df_WwT["Mean_Wealth"][i],                              y1 = i,                              line=dict(color='crimson', width = 3))fig3.update_layout(title_text =                    "Mean vs Median (U$S) Top 15 Countries",                    title_font_size = 30)fig3.update_xaxes(range=[0, 600000])fig3.write_image(path + "figdumbbell1.png")fig3.show()
The code for Fig.4 is the same that for Fig.3 except that we used df_WwB instead of df_WwT.
When you have a normally distributed sample you can use the mean or the median as your measure of central tendency. In any symmetrical distribution the mean, median, and mode are equal. This is not the case with our data (both, top 15 countries and bottom 15 countries): the mean is always greater than the median. It is a clear indication of right-skewed data. In these situations, the median is generally considered to be the best measure of central tendency. The median is not as strongly influenced by the skewed values of very wealthy people and provides the best central location for our data. The differences shown in our Dumbbell charts confirm this assumption.
Used exactly in the same situation as a standard bar chart, lollipop charts encode numerical values in the same way: the length of the lines and the location of the dots at the end of the lines are equivalent to the length or height of horizontal or vertical rectangular bars. Lollipop Charts are preferred to bar charts when you are dealing with a large number of similar numerical values. Dumbbell charts are simple graphs that quickly and directly show differences, changes, ranges, and similarities. They allow a significant amount of information to be coded into an easy-to-understand diagram.
If you find this article of interest, please read my previous (https://medium.com/@dar.wtz):
Diverging Bars, Why & How, Storytelling with Divergences
towardsdatascience.com
Slope Charts, Why & How, Storytelling with Slopes