MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr 🔥 tagline

Hey there 👋 I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 — present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 — Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms — earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

Python for Data Science: Getting Started

Begin your data science journey with Python: NumPy, Pandas, Matplotlib, and Jupyter notebooks.

PythonData ScienceNumPyPandas

By MinhVo

Introduction

Python has emerged as the undisputed leader in data science, powering everything from academic research to production machine learning systems at companies like Netflix, Spotify, and Instagram. Its intuitive syntax, combined with an extraordinarily rich ecosystem of scientific computing libraries, makes it the ideal language for anyone looking to extract insights from data. Whether you are a complete beginner or an experienced programmer transitioning into data science, Python provides the tools you need to go from raw data to actionable intelligence.

The data science landscape in 2024 and beyond is evolving rapidly, with new tools and frameworks emerging constantly. However, the foundational stack — NumPy for numerical computing, Pandas for data manipulation, Matplotlib for visualization, and Jupyter notebooks for interactive exploration — remains as relevant as ever. These four pillars form the backbone of virtually every data science project, from simple exploratory analysis to complex machine learning pipelines.

Python Data Science Ecosystem

In this comprehensive guide, we will take you from zero to productive data scientist with Python. You will learn how to set up your environment, master the core libraries, build real visualizations, and understand the workflows that professional data scientists use daily.

Understanding Python's Data Science Ecosystem

Python's dominance in data science is not accidental. The language was designed with readability and simplicity in mind, which lowers the barrier to entry for domain experts who are not professional programmers. Scientists, analysts, and engineers can quickly pick up Python and start extracting value from their data without wrestling with complex syntax.

The ecosystem grew organically around a few core needs. Scientists needed fast numerical computation — hence NumPy. Analysts needed intuitive data structures for tabular data — hence Pandas. Researchers needed publication-quality visualizations — hence Matplotlib. And everyone needed an interactive environment for exploratory work — hence Jupyter.

What makes this ecosystem truly powerful is how these libraries interoperate. NumPy arrays form the foundation that Pandas DataFrames are built on. Matplotlib integrates seamlessly with both. Jupyter ties everything together in an interactive environment. This tight integration means skills learned in one library transfer directly to others.

The Core Stack at a Glance

LibraryPurposeKey Data StructurePrimary Use Case
NumPyNumerical computingndarrayArray operations, linear algebra, random numbers
PandasData manipulationDataFrame, SeriesTabular data, time series, data cleaning
MatplotlibVisualizationFigure, AxesStatic plots, charts, publication graphics
JupyterInteractive computingNotebookExploration, prototyping, documentation
SeabornStatistical visualizationBuilt on MatplotlibDistribution plots, heatmaps, regression plots
scikit-learnMachine learningEstimator, PipelineClassification, regression, clustering

Setting Up Your Data Science Environment

Before diving into code, you need a properly configured environment. The two most popular approaches are Anaconda and pip with virtual environments. Anaconda is recommended for beginners because it bundles everything you need and handles dependency management automatically.

Installing Anaconda

Download Anaconda from the official website (anaconda.com). It includes Python, Jupyter, and over 250 popular data science packages pre-installed. After installation, verify it works:

# Verify installation
conda --version
python --version
jupyter --version

Alternative: pip with virtual environments

If you prefer a lighter setup, use pip with virtual environments:

# Create a virtual environment
python -m venv datascience_env
 
# Activate it
source datascience_env/bin/activate  # macOS/Linux
datascience_env\Scripts\activate     # Windows
 
# Install core packages
pip install numpy pandas matplotlib jupyter seaborn scikit-learn

Launching Jupyter Notebook

Jupyter notebooks provide an interactive environment where you can write code, see results, and add documentation all in one place:

# Start Jupyter Notebook
jupyter notebook
 
# Or use JupyterLab (modern interface)
jupyter lab

This opens a browser-based interface where you can create new notebooks, write code cells, and see output immediately. The cell-based execution model is perfect for data science because it lets you iterate quickly — run a cell, inspect the output, adjust your approach, and run it again.

Data Science Workspace

NumPy: The Foundation of Scientific Computing

NumPy (Numerical Python) is the bedrock of Python's scientific computing stack. It provides the ndarray, a fast, memory-efficient multi-dimensional array that is significantly faster than Python's built-in lists for numerical operations. Under the hood, NumPy uses optimized C and Fortran code, which is why operations on NumPy arrays can be orders of magnitude faster than equivalent Python loops.

Creating Arrays

There are multiple ways to create NumPy arrays depending on your needs:

import numpy as np
 
# From a Python list
arr = np.array([1, 2, 3, 4, 5])
print(arr.dtype)  # int64
 
# 2D array (matrix)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(matrix.shape)  # (2, 3)
 
# Arrays filled with zeros or ones
zeros = np.zeros((3, 4))
ones = np.ones((2, 5))
 
# Evenly spaced values
range_arr = np.arange(0, 10, 2)  # [0, 2, 4, 6, 8]
linspace_arr = np.linspace(0, 1, 5)  # [0.0, 0.25, 0.5, 0.75, 1.0]
 
# Random numbers
random_arr = np.random.randn(3, 3)  # Standard normal distribution
uniform_arr = np.random.uniform(0, 10, size=(2, 4))

Vectorized Operations

The real power of NumPy comes from vectorized operations — performing operations on entire arrays without explicit loops:

# Element-wise operations (no loops needed!)
a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])
 
print(a + b)   # [11, 22, 33, 44, 55]
print(a * b)   # [10, 40, 90, 160, 250]
print(a ** 2)  # [1, 4, 9, 16, 25]
print(np.sqrt(a))  # [1.0, 1.414, 1.732, 2.0, 2.236]
 
# Aggregations
print(np.mean(a))    # 3.0
print(np.std(a))     # ~1.414
print(np.sum(a))     # 15
 
# Broadcasting: operations between arrays of different shapes
matrix = np.array([[1, 2, 3], [4, 5, 6]])
vector = np.array([10, 20, 30])
result = matrix + vector  # Adds vector to each row automatically

Indexing, Slicing, and Boolean Filtering

NumPy provides powerful indexing capabilities that go far beyond Python lists:

arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
 
# Basic slicing
print(arr[2:7])  # [30, 40, 50, 60, 70]
 
# Boolean indexing (filtering)
mask = arr > 50
print(arr[mask])  # [60, 70, 80, 90, 100]
 
# Fancy indexing
indices = [1, 3, 5]
print(arr[indices])  # [20, 40, 60]
 
# 2D indexing
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(matrix[1, 2])       # 6 (row 1, column 2)
print(matrix[:, 0])        # [1, 4, 7] (all rows, column 0)
print(matrix[0:2, 1:3])   # [[2, 3], [5, 6]]

Linear Algebra

NumPy includes comprehensive linear algebra capabilities essential for data science and machine learning:

# Matrix multiplication
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
C = A @ B  # or np.dot(A, B)
 
# Determinant
det = np.linalg.det(A)
 
# Eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)
 
# Solve linear equations Ax = b
b = np.array([5, 11])
x = np.linalg.solve(A, b)
 
# Inverse
A_inv = np.linalg.inv(A)

Pandas: Data Manipulation Made Easy

Pandas is the go-to library for working with tabular data. Its DataFrame object provides an intuitive, spreadsheet-like interface for loading, cleaning, transforming, and analyzing structured data. If NumPy is the engine, Pandas is the dashboard — it makes complex data operations accessible and readable.

DataFrames and Series

A DataFrame is a two-dimensional labeled data structure with columns of potentially different types. Think of it as a programmable spreadsheet:

import pandas as pd
 
# Creating a DataFrame from a dictionary
data = {
    'name': ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'],
    'age': [28, 34, 45, 23, 31],
    'salary': [75000, 82000, 120000, 55000, 95000],
    'department': ['Engineering', 'Marketing', 'Engineering', 'Sales', 'Engineering']
}
df = pd.DataFrame(data)
 
# Basic exploration
print(df.head())       # First 5 rows
print(df.info())       # Column types and non-null counts
print(df.describe())   # Statistical summary
print(df.shape)        # (5, 4)

Loading Data from Various Sources

Pandas can read from dozens of file formats. The most common are CSV, Excel, and JSON:

# From CSV
df = pd.read_csv('data.csv')
df = pd.read_csv('data.csv', parse_dates=['date'], index_col='id')
 
# From Excel
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
 
# From JSON
df = pd.read_json('data.json')
 
# From SQL database
import sqlite3
conn = sqlite3.connect('database.db')
df = pd.read_sql('SELECT * FROM users', conn)
 
# From URL
df = pd.read_csv('https://example.com/data.csv')

Data Selection and Filtering

Selecting and filtering data is the most common operation in data analysis:

# Select columns
names = df['name']
subset = df[['name', 'salary']]
 
# Filter rows
high_earners = df[df['salary'] > 80000]
engineers = df[df['department'] == 'Engineering']
 
# Multiple conditions
senior_engineers = df[
    (df['department'] == 'Engineering') & (df['age'] > 30)
]
 
# loc (label-based) and iloc (position-based)
df.loc[0:2, 'name':'salary']
df.iloc[0:3, 0:2]
 
# Query method (SQL-like syntax)
result = df.query('salary > 80000 and department == "Engineering"')

Grouping and Aggregation

GroupBy operations let you split data into groups, apply functions, and combine results:

# Group by department
dept_stats = df.groupby('department').agg({
    'salary': ['mean', 'median', 'std'],
    'age': ['mean', 'min', 'max'],
    'name': 'count'
})
 
# Transform (broadcast group-level results back)
df['salary_pct'] = df.groupby('department')['salary'].transform(
    lambda x: x / x.sum() * 100
)
 
# Apply custom functions
def salary_range(group):
    return group['salary'].max() - group['salary'].min()
 
df.groupby('department').apply(salary_range)

Data Cleaning

Real-world data is messy. Pandas provides powerful tools for cleaning it:

# Handle missing values
df.isnull().sum()                    # Count missing per column
df.dropna()                          # Drop rows with any missing
df.fillna(0)                         # Fill missing with 0
df['age'].fillna(df['age'].median(), inplace=True)
 
# Remove duplicates
df.drop_duplicates(inplace=True)
df.drop_duplicates(subset=['name'], keep='last')
 
# Type conversion
df['date'] = pd.to_datetime(df['date'])
df['category'] = df['category'].astype('category')
 
# String operations
df['name_upper'] = df['name'].str.upper()
df['email_domain'] = df['email'].str.split('@').str[1]

Data Visualization

Matplotlib: Visualizing Your Data

Matplotlib is Python's foundational plotting library. While newer libraries like Seaborn and Plotly offer higher-level interfaces, Matplotlib provides the flexibility to create any visualization you can imagine. Understanding Matplotlib gives you complete control over your plots.

Basic Plots

import matplotlib.pyplot as plt
 
# Line plot
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.figure(figsize=(10, 6))
plt.plot(x, y, label='sin(x)', color='blue', linewidth=2)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sine Wave')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
 
# Scatter plot
x = np.random.randn(100)
y = x * 2 + np.random.randn(100) * 0.5
plt.scatter(x, y, alpha=0.6, c=y, cmap='viridis')
plt.colorbar(label='y value')
plt.title('Scatter Plot with Color Mapping')
 
# Bar chart
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 12, 67, 34]
colors = ['#2ecc71', '#3498db', '#e74c3c', '#f39c12', '#9b59b6']
plt.bar(categories, values, color=colors)
 
# Histogram
data = np.random.randn(1000)
plt.hist(data, bins=30, edgecolor='black', alpha=0.7)
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.title('Distribution of Random Data')

Subplots and Advanced Layouts

fig, axes = plt.subplots(2, 2, figsize=(12, 10))
 
axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('Sin(x)')
 
axes[0, 1].plot(x, np.cos(x), color='orange')
axes[0, 1].set_title('Cos(x)')
 
axes[1, 0].scatter(np.random.randn(50), np.random.randn(50))
axes[1, 0].set_title('Scatter')
 
axes[1, 1].bar(['A', 'B', 'C'], [3, 7, 5])
axes[1, 1].set_title('Bar')
 
plt.tight_layout()
plt.show()

Pandas Integration

Pandas DataFrames have built-in plotting that uses Matplotlib under the hood:

# Quick plots from DataFrames
df['salary'].hist(bins=20)
df.plot(x='age', y='salary', kind='scatter')
df.groupby('department')['salary'].mean().plot(kind='bar')
 
# Customizing Pandas plots
ax = df.groupby('department')['salary'].mean().plot(
    kind='barh',
    figsize=(10, 6),
    color='steelblue',
    edgecolor='black'
)
ax.set_xlabel('Average Salary ($)')
ax.set_title('Average Salary by Department')

Real-World Use Cases

Use Case 1: Customer Segmentation

A retail company uses Pandas and NumPy to segment customers based on purchasing behavior. By computing RFM (Recency, Frequency, Monetary) scores from transaction data, they identify high-value customers, churn risks, and growth opportunities. The entire pipeline — from raw CSV to actionable segments — runs in a Jupyter notebook.

Use Case 2: Financial Time Series Analysis

An investment firm analyzes stock price data using NumPy for numerical calculations and Pandas for time series manipulation. They compute moving averages, volatility measures, and correlation matrices across portfolios. Matplotlib generates the performance dashboards that portfolio managers review daily.

Use Case 3: A/B Test Analysis

A product team runs experiments and uses Python to analyze results. They compute conversion rates, confidence intervals, and statistical significance using SciPy (built on NumPy). Pandas handles the data wrangling, and Matplotlib visualizes the results for stakeholder presentations.

Use Case 4: Sensor Data Processing

A manufacturing company processes IoT sensor data from factory equipment. NumPy handles the numerical transformations, Pandas manages the time-indexed data streams, and Matplotlib generates real-time monitoring dashboards that help engineers detect anomalies before they cause failures.

Step-by-Step Implementation: End-to-End Analysis

Let's tie everything together with a realistic data science workflow.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
 
# Step 1: Load and explore
df = pd.read_csv('transactions.csv', parse_dates=['transaction_date'])
print(f"Shape: {df.shape}")
print(f"Missing values:\n{df.isnull().sum()}")
 
# Step 2: Clean and prepare
df['amount'].fillna(df['amount'].median(), inplace=True)
Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
mask = (df['amount'] >= Q1 - 1.5 * IQR) & (df['amount'] <= Q3 + 1.5 * IQR)
df_clean = df[mask]
 
# Step 3: Feature engineering
df_clean['month'] = df_clean['transaction_date'].dt.month
df_clean['day_of_week'] = df_clean['transaction_date'].dt.dayofweek
 
# Step 4: Analyze
monthly = df_clean.groupby('month')['amount'].sum()
monthly.plot(kind='line', marker='o', figsize=(10, 6))
plt.title('Monthly Revenue Trend')
plt.ylabel('Total Revenue ($)')
plt.grid(True, alpha=0.3)
plt.show()
 
# Step 5: Customer segmentation
customer_stats = df_clean.groupby('customer_id').agg({
    'amount': ['sum', 'mean', 'count']
})
customer_stats.columns = ['total_spent', 'avg_order', 'num_orders']
top_customers = customer_stats.nlargest(10, 'total_spent')
print(top_customers)

Best Practices for Production

  1. Start with exploration, not modeling: Always spend time understanding your data before building models. Use df.info(), df.describe(), and visualizations to get a feel for distributions, correlations, and anomalies.

  2. Use vectorized operations: Avoid Python loops whenever possible. NumPy and Pandas vectorized operations are 10-100x faster because they use optimized C code under the hood.

  3. Document your notebook: Use Markdown cells liberally to explain your thought process. Include sections for data source, cleaning steps, assumptions, and conclusions.

  4. Version control your data and code: Use Git for your notebooks and scripts. Consider DVC (Data Version Control) for large datasets. Never modify raw data files.

  5. Set random seeds for reproducibility: Always use np.random.seed(42) or random_state=42 in your analyses so anyone can reproduce your exact results.

  6. Handle missing data thoughtfully: Understand why data is missing — MCAR, MAR, or MNAR — and choose your strategy accordingly rather than blindly dropping or filling.

  7. Profile your data quality: Check for duplicate rows, inconsistent categories, impossible values, and data type mismatches before analysis.

  8. Use memory efficiently: Specify dtypes explicitly, use categorical types for low-cardinality strings, and consider chunked reading with pd.read_csv(..., chunksize=10000).

Common Pitfalls and Solutions

PitfallImpactSolution
Using loops instead of vectorization10-100x slower executionUse NumPy/Pandas vectorized operations
Not handling missing values earlyIncorrect analysis resultsProfile missing data immediately after loading
Ignoring data typesMemory waste, incorrect aggregationsUse df.info() and convert types explicitly
Modifying data in place accidentallyUnrecoverable data corruptionUse .copy() when creating subsets
Not setting random seedsNon-reproducible resultsAlways set random_state or np.random.seed()
Using inplace=True carelesslyChain operation issuesPrefer assignment: df = df.dropna()

Performance Optimization

When working with large datasets, performance matters significantly:

# Specify dtypes to reduce memory
dtypes = {'id': 'int32', 'category': 'category', 'amount': 'float32'}
df = pd.read_csv('large_file.csv', dtype=dtypes)
 
# Use categorical for repeated strings
df['department'] = df['department'].astype('category')
 
# Chunked processing for huge files
chunks = pd.read_csv('huge_file.csv', chunksize=50000)
results = []
for chunk in chunks:
    processed = chunk.groupby('category')['amount'].sum()
    results.append(processed)
final = pd.concat(results).groupby(level=0).sum()
 
# Use eval() for complex expressions
df.eval('profit = revenue - cost', inplace=True)
 
# Numba for custom numerical functions
from numba import jit
 
@jit(nopython=True)
def custom_calculation(arr):
    result = np.empty_like(arr)
    for i in range(len(arr)):
        result[i] = arr[i] ** 2 + np.sin(arr[i])
    return result

Comparison with Alternatives

FeaturePython + PandasR + tidyverseSQLExcel
Learning CurveModerateModerateEasyEasy
Data Size LimitMillions of rowsMillions of rowsBillions~1M rows
VisualizationMatplotlib, Seabornggplot2LimitedBuilt-in
ReproducibilityExcellentExcellentGoodPoor
Statistical TestsSciPy, statsmodelsBuilt-inLimitedAdd-ins
Machine Learningscikit-learn, TensorFlowcaret, tidymodelsNoneNone
DeploymentFlask, FastAPIShinyNativeNone

Advanced Patterns and Techniques

# Method chaining for clean data pipelines
result = (
    df
    .query('department == "Engineering"')
    .assign(bonus=lambda x: x['salary'] * 0.1)
    .groupby('team')
    .agg({'salary': 'mean', 'bonus': 'sum'})
    .sort_values('salary', ascending=False)
    .reset_index()
)
 
# Window functions
df['rolling_avg'] = df.groupby('customer')['amount'].transform(
    lambda x: x.rolling(window=7, min_periods=1).mean()
)
 
# Pivot tables
pivot = df.pivot_table(
    values='amount',
    index='department',
    columns='month',
    aggfunc=['mean', 'sum'],
    fill_value=0
)
 
# Multi-index operations
df.set_index(['department', 'team', 'employee']).sort_index()

Testing Strategies

import pytest
 
def test_salary_filter():
    data = {'name': ['A', 'B', 'C'], 'salary': [50000, 80000, 120000]}
    df = pd.DataFrame(data)
    result = df[df['salary'] > 70000]
    assert len(result) == 2
    assert result['name'].tolist() == ['B', 'C']
 
def test_missing_value_handling():
    data = {'age': [25, None, 35, None]}
    df = pd.DataFrame(data)
    df['age'].fillna(df['age'].median(), inplace=True)
    assert df['age'].isnull().sum() == 0
 
def test_groupby_aggregation():
    data = {'dept': ['A', 'A', 'B'], 'salary': [100, 200, 150]}
    df = pd.DataFrame(data)
    result = df.groupby('dept')['salary'].sum()
    assert result['A'] == 300
    assert result['B'] == 150

Future Outlook

The Python data science ecosystem continues to evolve rapidly. Polars, a Rust-based DataFrame library, is gaining traction for its speed and memory efficiency. DuckDB brings SQL-native analytics to Python. Great Expectations standardizes data quality checks. The rise of LLMs is also transforming data science — tools like LangChain and LlamaIndex make it easier than ever to build AI-powered data applications.

Despite these innovations, the fundamentals covered in this guide remain essential. NumPy, Pandas, Matplotlib, and Jupyter form the foundation that everything else builds upon.

Conclusion

Python for data science starts with understanding the core tools — NumPy for fast numerical computing, Pandas for intuitive data manipulation, Matplotlib for visualization, and Jupyter for interactive exploration. These libraries form an integrated ecosystem that makes Python the most productive environment for data analysis.

Key takeaways:

  1. Set up your environment properly with Anaconda or pip plus virtual environments
  2. Master NumPy fundamentals — arrays, vectorization, and broadcasting are the building blocks
  3. Learn Pandas deeply — DataFrames, groupby, and data cleaning are your daily tools
  4. Visualize everything with Matplotlib — plots reveal patterns that tables hide
  5. Use Jupyter notebooks for exploration, prototyping, and sharing your work
  6. Follow best practices — handle missing data, use vectorized operations, and document your process
  7. Test your data pipelines to catch bugs early and ensure correctness

Start small. Load a CSV, explore it with Pandas, create a few plots, and iterate from there. The barrier to entry is low, and the rewards — both intellectual and career-wise — are enormous.