Skip to content

API Reference

This page is auto-generated from Python docstrings.

Early on, it may not be easy to read. It becomes more useful as we gain experience.

bizintel

Bintel package.

bizintel.app_case

app_case.py - example.

An example of loading and visualizing raw business data to.

Author: Denise Case Date: 2026-06

Process
  • Load raw CSV data files.
  • Visualize sales by region and product category.
  • Log a summary of findings.

Data Source: - data/raw/customers_data.csv - data/raw/products_data.csv - data/raw/sales_data.csv

Terminal command to run this file from the root project folder:

uv run python -m bizintel.app_case

OBS

Don't edit this file - it should remain a working example. Copy it, rename it with your alias, and modify your copy. If you do, include your command to run it in the docstring above and in README.md.

CUSTOMERS_FILE module-attribute

CUSTOMERS_FILE: Final[Path] = (
    DATA_RAW / 'customers_data.csv'
)

DATA_RAW module-attribute

DATA_RAW: Final[Path] = Path('data/raw')

LOG module-attribute

LOG = get_logger('BI', level='DEBUG')

PRODUCTS_FILE module-attribute

PRODUCTS_FILE: Final[Path] = DATA_RAW / 'products_data.csv'

SALES_FILE module-attribute

SALES_FILE: Final[Path] = DATA_RAW / 'sales_data.csv'

load_data

load_data(filepath: Path, name: str) -> pd.DataFrame

Load one CSV file into a pandas DataFrame.

WHY: Reading from CSV into a DataFrame is the first step in almost every BI pipeline.

Parameters:

Name Type Description Default
filepath Path

Path to the CSV file.

required
name str

A short name for logging.

required

Returns:

Type Description
DataFrame

A pandas DataFrame with the file contents.

Source code in src/bizintel/utils_data.py
def load_data(filepath: Path, name: str) -> pd.DataFrame:
    """Load one CSV file into a pandas DataFrame.

    WHY: Reading from CSV into a DataFrame is the first step
    in almost every BI pipeline.


    Args:
        filepath: Path to the CSV file.
        name: A short name for logging.

    Returns:
        A pandas DataFrame with the file contents.
    """
    LOG.info(f"Loading {name} from {filepath}")
    df: pd.DataFrame = pd.read_csv(filepath)
    LOG.info(f"Loaded: {df.shape[0]} rows, {df.shape[1]} columns")
    return df

main

main() -> None

Main function to run the BI logic. This is where the main logic starts when this script is run.

Source code in src/bizintel/app_case.py
def main() -> None:
    """Main function to run the BI logic.
    This is where the main logic starts
    when this script is run.
    """

    # First, log the header for the BI module to indicate the start of the workflow.
    log_header(LOG, "BI")

    # Clearly indicate the start of the main function in the logs for easy tracking.
    LOG.info("========================")
    LOG.info("START main()")
    LOG.info("========================")

    # Use the imported log_path function to
    # log the paths of all critical paths and files for reference.
    log_path(LOG, "Raw data: ", DATA_RAW)
    log_path(LOG, "Customers:", CUSTOMERS_FILE)
    log_path(LOG, "Products: ", PRODUCTS_FILE)
    log_path(LOG, "Sales:    ", SALES_FILE)

    LOG.info("CALL a function to load each dataset.............")
    df_customers = load_data(CUSTOMERS_FILE, "customers")
    df_products = load_data(PRODUCTS_FILE, "products")
    df_sales = load_data(SALES_FILE, "sales")

    LOG.info("CALL a function to get sales by region........")
    df_region = sales_by_region(df_customers, df_sales)

    LOG.info("CALL a function to plot sales by region........")
    plot_bar(
        df=df_region,
        x="Region",
        y="SaleAmount",
        title="Total Sales by Region",
        xlabel="Region",
        ylabel="Total Sales Amount ($)",
        palette="Blues_d",
    )

    LOG.info("CALL a function to get sales by product category........")
    df_category = sales_by_category(df_products, df_sales)

    LOG.info("CALL a function to plot sales by product category........")
    plot_bar(
        df=df_category,
        x="Category",
        y="SaleAmount",
        title="Total Sales by Product Category",
        xlabel="Category",
        ylabel="Total Sales Amount ($)",
        palette="Greens_d",
    )

    LOG.info("CALL a function to summarize the datasets........")
    summarize(df_customers, df_products, df_sales)

    LOG.info("CALL a function to show charts........")
    plt.show()

    LOG.info("Workflow complete")
    LOG.info("CLOSE chart windows to continue.")
    LOG.info("Terminate this process with CTRL+c as needed.")
    LOG.info("========================")
    LOG.info("Executed successfully!")
    LOG.info("========================")

plot_bar

plot_bar(
    df: DataFrame,
    x: str,
    y: str,
    title: str,
    xlabel: str,
    ylabel: str,
    palette: str = 'Blues_d',
) -> None

Plot a vertical bar chart.

WHY: Bar charts are the most common chart in BI reporting. A reusable function ensures consistent style and labeling across all modules.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the data to plot.

required
x str

Column name for the x-axis (categories).

required
y str

Column name for the y-axis (values).

required
title str

Chart title.

required
xlabel str

X-axis label.

required
ylabel str

Y-axis label.

required
palette str

Seaborn color palette name.

'Blues_d'

Returns:

Type Description
None

None

Source code in src/bizintel/utils_viz.py
def plot_bar(
    df: pd.DataFrame,
    x: str,
    y: str,
    title: str,
    xlabel: str,
    ylabel: str,
    palette: str = "Blues_d",
) -> None:
    """Plot a vertical bar chart.

    WHY: Bar charts are the most common chart in BI reporting.
    A reusable function ensures consistent style and labeling
    across all modules.

    Args:
        df: DataFrame containing the data to plot.
        x: Column name for the x-axis (categories).
        y: Column name for the y-axis (values).
        title: Chart title.
        xlabel: X-axis label.
        ylabel: Y-axis label.
        palette: Seaborn color palette name.

    Returns:
        None
    """
    LOG.info(f"Creating chart: {title}")

    _, ax = plt.subplots(figsize=(9, 5))

    bar: Axes = sns.barplot(
        data=df,
        x=x,
        y=y,
        hue=x,
        legend=False,
        palette=palette,
        ax=ax,
    )

    bar.set_title(f"{title} (CLOSE chart to continue)")
    bar.set_xlabel(xlabel)
    bar.set_ylabel(ylabel)

    plt.tight_layout()

sales_by_category

sales_by_category(
    df_products: DataFrame, df_sales: DataFrame
) -> pd.DataFrame

Aggregate total sales amount by product category.

WHY: Product category is another key business dimension. Understanding which categories drive revenue helps prioritize inventory, marketing, and purchasing decisions.

Parameters:

Name Type Description Default
df_products DataFrame

Products DataFrame with ProductID and Category columns.

required
df_sales DataFrame

Sales DataFrame with ProductID and SaleAmount columns.

required

Returns:

Type Description
DataFrame

DataFrame with Category and SaleAmount columns, sorted by SaleAmount.

Source code in src/bizintel/app_case.py
def sales_by_category(
    df_products: pd.DataFrame,
    df_sales: pd.DataFrame,
) -> pd.DataFrame:
    """Aggregate total sales amount by product category.

    WHY: Product category is another key business dimension.
    Understanding which categories drive revenue helps prioritize
    inventory, marketing, and purchasing decisions.

    Args:
        df_products: Products DataFrame with ProductID and Category columns.
        df_sales: Sales DataFrame with ProductID and SaleAmount columns.

    Returns:
        DataFrame with Category and SaleAmount columns, sorted by SaleAmount.
    """
    LOG.info("Aggregating sales by product category")

    # Make a copy of the sales DataFrame to avoid modifying the original
    df_sales = df_sales.copy()

    # Convert the SaleAmount column to numeric, coercing errors to NaN ("not a number")
    df_sales["SaleAmount"] = pd.to_numeric(df_sales["SaleAmount"], errors="coerce")

    # Merge the sales DataFrame with the products DataFrame on ProductID
    # to get the Category for each sale (like a "left join" in SQL)
    # A left join means we keep all rows from df_sales and add matching Category values from df_products.
    df_merged: pd.DataFrame = df_sales.merge(
        df_products[["ProductID", "Category"]],
        on="ProductID",
        how="left",
    )

    # Group the merged DataFrame by Category and sum the SaleAmount for each category.
    # This returns a Series (a single column of values, one per category).
    # We cast to Series because we are grouping a single column.
    grouped: pd.Series = pd.Series(df_merged.groupby("Category")["SaleAmount"].sum())

    # Reset the index to turn the Series back into a DataFrame with two columns:
    # Category and SaleAmount.
    # Then sort by SaleAmount descending so the highest-revenue category appears first.
    df_category: pd.DataFrame = grouped.reset_index().sort_values(
        "SaleAmount", ascending=False
    )

    # Use the built-in dataframe iloc (index location) method
    # to get the first row of the sorted DataFrame (the category with the highest sales)
    # as a string.
    # In Python, we start counting with 0 (no offset from the beginning of the list),
    # so the first row is at index 0.
    top_category: str = str(df_category.iloc[0]["Category"])

    # Use the built-in dataframe iloc (index location) method
    # to get the first row of the sorted DataFrame
    # (the category with the highest sales)
    # as a float.
    top_sales: float = float(df_category.iloc[0]["SaleAmount"])

    # Log the top category and its total sales amount for quick reference
    # Using handy dandy f-strings (formatted string literals).
    # Format the sales amount as currency with commas and two floating decimal places.
    LOG.info(f"  Top category: {top_category} (${top_sales:,.2f})")

    return df_category

sales_by_region

sales_by_region(
    df_customers: DataFrame, df_sales: DataFrame
) -> pd.DataFrame

Aggregate total sales amount by customer region.

Parameters:

Name Type Description Default
df_customers DataFrame

Customers DataFrame with CustomerID and Region columns.

required
df_sales DataFrame

Sales DataFrame with CustomerID and SaleAmount columns.

required

Returns:

Type Description
DataFrame

DataFrame with Region and SaleAmount columns, sorted by SaleAmount.

Source code in src/bizintel/app_case.py
def sales_by_region(
    df_customers: pd.DataFrame,
    df_sales: pd.DataFrame,
) -> pd.DataFrame:
    """Aggregate total sales amount by customer region.

    Args:
        df_customers: Customers DataFrame with CustomerID and Region columns.
        df_sales: Sales DataFrame with CustomerID and SaleAmount columns.

    Returns:
        DataFrame with Region and SaleAmount columns, sorted by SaleAmount.
    """
    LOG.info("Aggregating sales by region")

    # Make a copy of the sales DataFrame to avoid modifying the original
    df_sales = df_sales.copy()

    # Convert the SaleAmount column to numeric, coercing errors to NaN ("not a number")
    df_sales["SaleAmount"] = pd.to_numeric(df_sales["SaleAmount"], errors="coerce")

    # Merge the sales DataFrame with the customers DataFrame on CustomerID
    # to get the Region for each sale (like a "left join" in SQL)
    # A left join means we keep all rows from df_sales and add matching Region values from df_customers.
    df_merged: pd.DataFrame = df_sales.merge(
        df_customers[["CustomerID", "Region"]],
        on="CustomerID",
        how="left",
    )

    # Clean up the Region column by stripping whitespace and capitalizing each word
    df_merged["Region"] = df_merged["Region"].str.strip().str.title()

    # Group the merged DataFrame by Region and sum the SaleAmount for each region.
    # This returns a Series (a single column of values, one per region).
    # We cast to Series because we are grouping a single column.
    grouped: pd.Series = pd.Series(df_merged.groupby("Region")["SaleAmount"].sum())

    # Reset the index to turn the Series back into a DataFrame with two columns:
    # Region and SaleAmount.
    # Then sort by SaleAmount descending so the highest-revenue region appears first.
    df_region: pd.DataFrame = grouped.reset_index().sort_values(
        "SaleAmount", ascending=False
    )

    # Use the built-in dataframe iloc (index location) method
    # to get the first row of the sorted DataFrame (the region with the highest sales)
    # as a string.
    # In Python, we start counting with 0
    # (no offset from the beginning of the list),
    # so the first row is at index 0.
    top_region: str = str(df_region.iloc[0]["Region"])

    # Use the built-in dataframe iloc (index location) method
    # to get the first row of the sorted DataFrame
    # (the region with the highest sales)
    # as a float.
    top_sales: float = float(df_region.iloc[0]["SaleAmount"])

    # Log the top region and its total sales amount for quick reference
    # Using handy dandy f-strings (formatted string literals).
    # Format the sales amount as currency with commas and two floating decimal places.
    LOG.info(f"  Top region: {top_region} (${top_sales:,.2f})")

    LOG.info("Returning DataFrame with total sales by region")
    return df_region

summarize

summarize(
    df_customers: DataFrame,
    df_products: DataFrame,
    df_sales: DataFrame,
) -> None

Log a brief summary of all three datasets.

Parameters:

Name Type Description Default
df_customers DataFrame

Customers DataFrame.

required
df_products DataFrame

Products DataFrame.

required
df_sales DataFrame

Sales DataFrame.

required

Returns:

Type Description
None

None

Source code in src/bizintel/app_case.py
def summarize(
    df_customers: pd.DataFrame,
    df_products: pd.DataFrame,
    df_sales: pd.DataFrame,
) -> None:
    """Log a brief summary of all three datasets.

    Args:
        df_customers: Customers DataFrame.
        df_products: Products DataFrame.
        df_sales: Sales DataFrame.

    Returns:
        None
    """
    LOG.info("========================")
    LOG.info("SUMMARY")
    LOG.info("========================")

    # Get the number of rows and columns in each using the shape attribute (0=rows, 1=columns)
    cust_rows: int = df_customers.shape[0]
    cust_cols: int = df_customers.shape[1]

    # Get the number of rows and columns in the products DataFrame using the shape attribute (0=rows, 1=columns)
    prod_rows: int = df_products.shape[0]
    prod_cols: int = df_products.shape[1]

    # Get the number of rows and columns in the sales DataFrame using the shape attribute (0=rows, 1=columns)
    sale_rows: int = df_sales.shape[0]
    sale_cols: int = df_sales.shape[1]

    # Log the summary of each dataset using f-strings (formatted string literals)
    # Start with an f outside the string, then use curly braces {} to insert variables into the string.
    LOG.info(f"Customers:  {cust_rows} rows, {cust_cols} columns")
    LOG.info(f"Products:   {prod_rows} rows, {prod_cols} columns")
    LOG.info(f"Sales:      {sale_rows} rows, {sale_cols} columns")

    LOG.info("========================")
    LOG.info("ANALYST NOTES:")
    LOG.info("Note any data quality issues.")
    LOG.info("We will clean data later.")
    LOG.info("========================")