The ExPdPy Shiny app

No installation required — try the app live in your browser:

☁️ Launch ExPdPy on shinyapps.io →

The hosted app runs Shiny for Python on shinyapps.io and starts with a file-upload dialog — there is no bundled data, so you bring your own CSV / Excel / Parquet. To try it with the panel used throughout these docs:

  1. Download the sample: kuznets.csv (880 rows × 21 columns).
  2. Open the live app and use Upload to select kuznets.csv.
  3. Explore the descriptive tables, distributions, correlations and scatter (the N-shaped Kuznets curve), and the regression builder.
Warning

The hosted free-tier instance has limited compute time and memory, so heavy operations may be slow or time out, and the app may sleep when idle. For the full, fast experience — and larger datasets — run the app locally (install and launch instructions below).

The ExPdPy app wraps the analytical functions in an interactive, no-code interface (built with Shiny for Python). It is provided by the app extra. The package is not on PyPI yet, so install it from GitHub:

pip install "expdpy[app] @ git+https://github.com/cmg777/expdpy.git"
# or, with uv:
uv pip install "expdpy[app] @ git+https://github.com/cmg777/expdpy.git"

Once published to PyPI, pip install "expdpy[app]" will work directly.

Using the app

Launching on a DataFrame

from expdpy.app import ExPdPy
from expdpy.data import load_kuznets, load_kuznets_data_def, get_config

ExPdPy(
    load_kuznets(),
    df_def=load_kuznets_data_def(),     # identifies the panel dimensions (country x year)
    config_list=get_config("kuznets"),  # opens on the N-shaped Kuznets curve
)

This opens a browser with a sidebar (sample, subset filter, outlier treatment, save/load config, notebook export) and a stack of analysis cards: descriptive statistics, histogram, extreme observations, correlations, time trends, scatter plot, by-group views and a regression builder.

Cross-sectional data

If you do not provide a time-series identifier, the time-trend components are dropped. For example, a single year of kuznets is cross-sectional:

ExPdPy(load_kuznets().query("year == 2025"))  # no ts_id -> cross-sectional

Starting from an upload dialog

Call ExPdPy() with no data to start with an in-app file upload (CSV/Excel/parquet):

ExPdPy()

Reproducible export

The Export notebook + data button downloads a zip containing the prepared analysis sample (parquet) plus a Jupyter notebook and a .py script that recreate every displayed component with expdpy calls — so an analysis done in the app can be reproduced in code.

Saving configurations

Save config downloads the current analysis configuration as JSON (optionally encrypted when store_encrypted=True); Load config restores it. You can also pass a configuration at launch:

from expdpy.data import load_kuznets, load_kuznets_data_def, get_config

ExPdPy(
    load_kuznets(),
    df_def=load_kuznets_data_def(),
    config_list=get_config("kuznets"),
)

Customizing the app

Selecting and ordering components

The components argument controls which analysis cards appear and in what order. Pass a list (order matters) or a {name: bool} mapping:

from expdpy.app import ExPdPy
from expdpy.data import load_kuznets, load_kuznets_data_def

ExPdPy(
    load_kuznets(),
    df_def=load_kuznets_data_def(),
    components=["descriptive_table", "scatter_plot", "regression"],
)

The available component names are:

bar_chart, missing_values, descriptive_table, histogram, ext_obs, by_group_bar_graph, by_group_violin_graph, trend_graph, quantile_trend_graph, by_group_trend_graph, corrplot, scatter_plot, regression.

(Time-series components are automatically dropped for cross-sectional data.)

Advanced mode — building analysis variables

Provide a var_def frame to compute the analysis sample from the base data. Each row’s var_def is a safe expression (column references plus + - * / ** %, comparisons, & |, and the functions isna, exp, log, lag, lead). lag/lead are panel-aware (they shift within cs_id groups ordered by ts_id).

import pandas as pd
from expdpy.app import ExPdPy
from expdpy.data import load_kuznets, load_kuznets_data_def

var_def = pd.DataFrame(
    {
        "var_name": ["country", "year", "gdp_pc_growth"],
        "var_def": ["country", "year", "(gdp_pc - lag(gdp_pc, 1)) / lag(gdp_pc, 1)"],
        "type": ["cs_id", "ts_id", "numeric"],
        "can_be_na": [False, False, True],
    }
)
ExPdPy(load_kuznets(), df_def=load_kuznets_data_def(), var_def=var_def)
Note

Expressions are evaluated with a restricted AST walker — never eval/exec. Attribute access, subscripting, lambdas and imports are rejected, which is strictly safer than R’s sandboxed eval.

Disabling app features

ExPdPy(
    load_kuznets(),
    df_def=load_kuznets_data_def(),
    export_nb_option=False,     # hide notebook export
    save_settings_option=False, # hide config save/load
)