Data Analysis Basics
10 examples to get you started with Data Analysis - 7 basic and 3 intermediate.
Prerequisites
- Python 3.14.0 with pandas 2.2+ and NumPy installed.
- Quick setup:
uv pip install "pandas>=2.2" "numpy>=2.0" "pyarrow>=18"
Basic Examples
1. NumPy Array Creation
Create a homogeneous numeric array and run vectorized math.
import numpy as np
sales = np.array([120, 340, 280, 410], dtype=np.int32)
bonus = sales * 0.1
print(bonus.dtype, bonus.sum())- NumPy arrays store one dtype per axis - mixing types forces coercion.
- Vectorized ops avoid Python loops and run in compiled C.
dtypecontrols memory and precision - pick it deliberately.
Related: NumPy Arrays - dtypes and broadcasting
2. pandas Series and DataFrame
Wrap labeled data in pandas structures.
import pandas as pd
revenue = pd.Series([120, 340, 280], index=["Jan", "Feb", "Mar"], name="revenue")
df = pd.DataFrame({"region": ["East", "West", "East"], "revenue": revenue.values})
print(df.dtypes)- A
Seriesis a single column with an index. - A
DataFrameis a table of aligned Series sharing the same index. .dtypesreveals whether columns are numeric, string, or categorical.
Related: pandas Series & DataFrames - construction and selection
3. Read CSV and Inspect
Load a file and profile it before transforming.
import pandas as pd
from io import StringIO
csv = "date,region,revenue\n2025-01-01,East,120\n2025-01-02,West,340\n"
df = pd.read_csv(StringIO(csv), parse_dates=["date"])
print(df.info())
print(df.isna().sum())parse_datesstores datetimes as proper dtypes, not strings.info()shows non-null counts and memory usage.- Count missing values per column before aggregating.
Related: Cleaning & Transforming Data - missing values and dtypes
4. Select Rows and Columns
Use label-based and position-based indexing.
import pandas as pd
df = pd.DataFrame({"region": ["East", "West", "East"], "revenue": [120, 340, 280]})
east = df.loc[df["region"] == "East", ["region", "revenue"]]
first_row = df.iloc[0].locselects by labels;.ilocselects by integer position.- Boolean masks filter rows without copying the whole frame when possible.
- Always assign filtered results to a new variable or use
.copy()if you will mutate.
Related: pandas Series & DataFrames - indexing rules
5. Handle Missing Values
Drop or fill gaps explicitly.
import pandas as pd
import numpy as np
df = pd.DataFrame({"revenue": [120, np.nan, 280]})
filled = df["revenue"].fillna(df["revenue"].median())
dropped = df.dropna(subset=["revenue"])fillnawith a statistic is fine for exploration; document the policy for production.dropnaremoves rows - verify you are not biasing aggregates.- pandas 2.x uses nullable dtypes (
Int64) when you need integers with NA.
Related: Cleaning & Transforming Data - dtype fixes
6. GroupBy Aggregation
Summarize by category with split-apply-combine.
import pandas as pd
df = pd.DataFrame({"region": ["East", "West", "East"], "revenue": [120, 340, 280]})
summary = (
df.groupby("region", observed=True)["revenue"]
.agg(["sum", "mean", "count"])
.reset_index()
)groupbysplits rows, applies a function per group, and combines results.observed=Trueskips unused categorical levels in pandas 2.x..reset_index()turns the group key back into a normal column.
Related: GroupBy & Aggregation - pivot tables
7. Merge Two Tables
Combine datasets on a shared key.
import pandas as pd
orders = pd.DataFrame({"order_id": [1, 2], "region": ["East", "West"]})
revenue = pd.DataFrame({"order_id": [1, 2], "amount": [120, 340]})
merged = orders.merge(revenue, on="order_id", how="inner", validate="one_to_one")how="inner"keeps only matching keys - document what you drop.validatecatches accidental many-to-many joins early.- Check row counts before and after every merge.
Related: Joins & Merges - duplicate detection
Intermediate Examples
8. Resample a Time Series
Aggregate daily data to monthly totals.
import pandas as pd
idx = pd.date_range("2025-01-01", periods=90, freq="D", tz="UTC")
df = pd.DataFrame({"revenue": range(90)}, index=idx)
monthly = df["revenue"].resample("ME").sum()- Datetime indexes enable calendar-aware grouping via
.resample. - Store timestamps in UTC; convert to local time only for display.
"ME"is month-end frequency in modern pandas.
Related: Time Series - rolling windows and timezones
9. Polars Lazy Scan
Defer work and let the query planner optimize I/O.
import polars as pl
lf = pl.scan_parquet("sales.parquet")
result = (
lf.filter(pl.col("region") == "East")
.group_by("month")
.agg(pl.col("revenue").sum().alias("total"))
.collect()
)
print(result)- Polars lazy frames push filters down before reading all columns.
.collect()executes the plan - keep logic lazy until you need materialized data.- Polars 1.x uses Apache Arrow memory layout for fast columnar ops.
Related: Polars - lazy evaluation patterns
10. Memory-Efficient dtypes
Shrink a wide frame before it exhausts RAM.
import pandas as pd
df = pd.read_csv("large.csv", dtype={"region": "category", "units": "int32"})
df["revenue"] = pd.to_numeric(df["revenue"], downcast="float")
print(df.memory_usage(deep=True).sum() / 1e6, "MB")categorystores repeated strings once - ideal for low-cardinality columns.downcastpicks the smallest numeric dtype that fits your values.- Measure with
memory_usage(deep=True)after every heavy load.
Related: Performance & Memory - chunking and Arrow
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.