FIELD NOTE · EDA

Creating Tables for Descriptive Analysis

When conduction EDA, multiple steps are required. When using python, other tools can be used to facilitate visualizations and other commands.

The first step in any data analysis is the discovery process. This is where we find the size, variable names, data types, and so on.

This is one of the most critical phase of the data analysis. Yet, it is the most tedious one. In the past, I’ve created steps different functions to automate some of the work.

The python ecosystem have tools that allow you to format DataFrames to give a more sophisticated look.

This comes handy for scholars when trying to create Tables for research papers. For desmonstration purposes, I used the Palmer Penguins dataset. I did not do any statistical analysis, this was for EDA purposes only.

Loading the Data

First, we need to install all of the packages we are going to use for this project. Note, some of these packages have changed names over time, and are likely to do again. If you are having trouble downloading one, you can always check out the PyPi website.

%pip install pandas seaborn skimpy tailbone great-tables fg-data-profiling

We can load the data from seaborn.

import pandas as pd
import seaborn as ins

df = sns.load_dataset("penguins")

After so many years doing this, the next commands have become almost a second nature.

print(df.shape)
print(df.dtypes)
print(df.isna().sum())

Getting the Baseline

We all know that Python has the built-in describe() function. That’s a good starting point.

df.describe().round(2)

This command is not very complete. It is missing several critical elements that would require me to run several other commands to get a full picture. For instance, it doesn’t have skewness or kurtosis.

Don’t get me wrong, pandas is more than capable to show us all of that information. With couple other commands, you can get more information from the data.

df.describe(include="all").round(2)

It provides a better output, but still not enough information. The looks of the table is not the best either. By the way, I hate when my tables have NaN.

We can also build a summary with .agg(). That comes handy when you want to control the statistics.

numeric = df.select_dtypes("number")
summary = numeric.agg(["mean", "median", "std", "skew", "kurt"]).T
summary["missing_pct"] = df[numeric.columns].isna().mean().mul(100).round(1)
summary.round(2)

That gives you a little more information and it is customizable. You can also use the .groupby() function, which will also provide a summary based on the specie of the penguin.

df.groupby("species")["body_mass_g"].describe().round(1)

Getting a More Robust Summary with skimpy

Skimpy is a supercharged describe(). It handles every column at once and it generates a nice report.

from skimpy import skim

skim(df)

Output:

╭─────────────────────────────────────────────────── skimpy summary ───────────────────────────────────────────────────╮
│          Data Summary                Data Types                                                                      │
│ ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┏━━━━━━━━━━━━━┳━━━━━━━┓                                                               │
│ ┃ Dataframe          Values ┃ ┃ Column Type  Count ┃                                                               │
│ ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩ ┡━━━━━━━━━━━━━╇━━━━━━━┩                                                               │
│ │ Number of rows    │ 344    │ │ float64     │ 4     │                                                               │
│ │ Number of columns │ 7      │ │ string      │ 3     │                                                               │
│ └───────────────────┴────────┘ └─────────────┴───────┘                                                               │
│                                                       number                                                         │
│ ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━━━┓  │
│ ┃ column               NA   NA %                 mean    sd      p0    p25    p50    p75   p100  hist   ┃  │
│ ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━━━┩  │
│ │   bill_length_mm     2 0.5813953488372093 43.92  5.4632.139.2344.4548.559.6▃█▆█▃  │  │
│ │    bill_depth_mm     2 0.5813953488372093 17.15 1.97513.1 15.6 17.318.721.5▄▅▆█▆▂ │  │
│ │  flipper_length_mm   2 0.5813953488372093 200.9 14.06 172  190  197 213 231▂██▄▆▃ │  │
│ │     body_mass_g      2 0.5813953488372093  4202   8022700 3550 405047506300▂█▆▄▃▁ │  │
│ └─────────────────────┴─────┴─────────────────────┴────────┴────────┴──────┴───────┴───────┴──────┴──────┴────────┘  │
│                                                       string                                                         │
│ ┏━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┓  │
│ ┃                                                                   chars per    words per    total      ┃  │
│ ┃ column   NA  NA %         shortest  longest    min     max        row          row          words      ┃  │
│ ┡━━━━━━━━━╇━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━┩  │
│ │ species 0          0 Adelie ChinstrapAdelie Gentoo         6.59          1       344 │  │
│ │ island  0          0 Dream  TorgersenBiscoeTorgersen       6.09          1       344 │  │
│ │   sex  113.197674418  Male   Female  Female  Male          4.99       0.97       333 │  │
│ │         │    │      604651 │          │           │        │           │             │             │            │  │
│ └─────────┴────┴─────────────┴──────────┴───────────┴────────┴───────────┴─────────────┴─────────────┴────────────┘  │
╰──────────────────────────────────────────────────────── End ─────────────────────────────────────────────────────────╯
The description, by default, ignores categorical data.

That’s a much nicer report.

Generating a Full Interactive Report with Profiling

Now, when you need something that will provide you a full picture, data-profiling will provide you that.

from data_profiling import ProfileReport
profile = ProfileReport(df, title="Penguins Profiling Report", explorative=True)

img

Conclusion

Using this function, can cut down on some time you spend running commands. I don’t consider this an automation as you still need to run multiple commands. It does help, however with visualization and nice looking tables.