UV、PIP、Pandas 和 NumPy 只是工具,思考才是數據分析師的真正工作
UV, PIP, Pandas, and NumPy for Data Analysts are just tools; thinking is the real Job.
UV、PIP、NumPy 和 Pandas 是 Python 數據分析工具,而分析師的核心工作是先理解問題、界定目標,再以分析思維將數據轉化為有用洞見。文章説明 PIP 的套件安裝與虛擬環境操作,並指出 UV 涵蓋 Python 版本、環境、依賴項目及套件管理等更廣泛的項目管理流程。
Today, we dive into the world of data analysis using Python. Our journey starts with setting up a Python virtual environment, managing packages, and transforming raw data into meaningful insights through analytical thinking which is what actually an analyst is paid for.
From the premise of the topic, you might wonder, if we are using Python, then what are UVs, PIPs, Pandas, and NumPy in our Heading? And I will simply answer you that these are tools built for Python that will help us reach our destination. UV and PIP help us manage our Python environment and tools, while NumPy and pandas help us turn data into something useful.
Every Data Journey starts with a solid plan
Imagine you have just landed a data analytics project. Your assignment is to analyse sales across several branches, identify the best-performing products, uncover revenue trends, and present recommendations to management. You have Python installed on your computer. You open VS Code, create a project folder, and write your first line of code.
Then reality hits.
Your code cannot find a library. You install a package, but another project stops working. You import a dataset, only to discover missing values, inconsistent records, and numbers that do not add up. Eventually, you realise that analysing data is not simply about writing Python code. It is about building a reliable environment and using the right tools to manipulate information.
This is where four important tools enter the story: UV, PIP, NumPy, and Pandas. Each plays a different role:
- UV: A fast Python project and package manager that can manage environments, dependencies, and Python versions.
- PIP: Python's traditional package installer, used to install and manage Python packages. (It's slow, so we mostly prefer UV)
- NumPy: A library for fast numerical computing, multidimensional arrays, and mathematical operations.
- Pandas: A library for working with structured data, particularly tables containing rows and columns.
However, your plan is in complete shambles. This is because before the tools comes the understanding of the problem, the accurate problem framing process, understanding the objectives of the analysis and what success would look like and only then can you start using the tools. How can you drive without knowing where you're going?
Now that we have established the journey plan, let's look at the tools. UV or PIP helps us prepare, organise, pack, and equip ourselves. NumPy and Pandas help us arrive at our destination (the vision we have established) as the tools for cleaning and analysing our data.
Understanding PIP
What is PIP?
PIP stands for Pip Installs Packages. It is the standard package installer commonly used in Python projects. Data professionals often need additional libraries not built into Python for specialised tasks. PIP makes this possible by helping us to download and install. Some of these packages are:
| Package | What it helps you do |
|---|---|
| NumPy | Numerical computation and array operations |
| Pandas | Data manipulation and analysis |
| Matplotlib | Data visualisation |
| Requests | Make HTTP requests |
| Scikit-learn | Machine learning |
| SQLAlchemy | Work with databases |
Installing a package using PIP
To install pandas and NumPy using PIP,
Open your terminal (PowerShell) and execute:
python -m pip install pandas numpy
Let's break this down:
-
pythonruns the Python interpreter. -
-m pipruns pip as a Python module, helping ensure you use the pip associated with that interpreter. -
installtells pip to install packages. -
pandas numpyidentifies the two packages you want.
Why use python -m pip instead of simply pip? On systems with multiple Python installations, the standalone pip command may point to a different interpreter. Using python -m pip makes the relationship more explicit.
Creating an isolated or virtual environment
Imagine project A requires pandas version 2.2, while project B requires a different version. Installing everything globally can create dependency conflicts. A virtual environment provides an isolated location for a project's Python packages. To create one:
python -m venv .venv #Creates the virtual environment
.\.venv\Scripts\Activate.ps1 #Activates it on Windows using PowerShell
Once activated, install the packages inside the environment using the installation code we used above. Now, your project has its own package environment, reducing interference from unrelated projects.
Just a few essential PIP operations I think you should know
python -m pip --version #Checking your pip version
python -m pip list #Checking the List of installed packages
python -m pip show pandas #Inspecting a particular package
python -m pip install --upgrade pandas #Upgrading a package
python -m pip uninstall pandas #Uninstalling a package
python -m pip check #Checking for dependency problems
When should you use PIP?
- You need a simple way to install Python packages.
- You are working with an existing project that uses
requirements.txt.
But what if you want a tool that brings project setup, Python version management, virtual environments, dependency resolution, and package installation together? Then PIP no longer cuts it. That brings us to UV.
UV
What is UV?
UV is a modern Python package and project manager. It is designed to be fast and to simplify several aspects of Python development. While pip primarily installs packages, UV offers a broader project-management workflow.
UV supports workflows that use pyproject.toml and uv.lock, while also offering commands for pip-compatible workflows.
UV can:
- Install and manage Python versions.
- Create and manage virtual environments.
- Add and remove project dependencies.
- Resolve and lock dependencies.
- Run commands within a project's environment.
- Install packages quickly, using caching and other optimisations.
Installing UV
On Windows, one installation option is (in PowerShell):
winget install --id
astral-sh.uv -e
Alternatively, follow the official installation instructions at:
https://docs.astral.sh/uv/getting-started/installation/
After installation, verify it:
Creating your first UV project
Going back to our sales analytics project.
Let's create a new project by initializing UV in the project folder:
Move into the directory (in the terminal):
cd sales-analytics
then,
uv init sales-analytics
UV creates a project structure like the one below:
sales-analytics/
├── .python-version
├── .venv/
├── main.py
├── pyproject.toml
└── uv.lock
Here is what the files mean:
-
.python-versionspecifies the project's intended Python version when generated. -
.venv/contains the virtual environment created for the project. -
main.pycontains your Python code. -
pyproject.tomlrecords project configuration and declared dependencies. -
uv.lockrecords the resolved dependency versions for reproducible installations.
The lockfile is particularly important when you collaborate with others or move a project between development and production.
Installing packages with UV
uv add pandas numpy
UV processes this and makes the packages available to the project environment.
To ensure the project environment is prepared, uses the created environment to execute your script; use this command:
uv run main.py
It helps you work without repeatedly activating the virtual environment manually.
Just a few essential UV Commands I think you should know
uv --version #Checking the installed UV version
uv python list #Listing Python installations managed by UV
uv python install 3.12 #Installing a particular Python version
uv python pin 3.12 #Pinning the project's Python version
uv add matplotlib #Adding a dependency
uv remove matplotlib #Removing a dependency
uv run main.py #Running a Python script in the project environment
uv run python -c "import pandas; print(pandas.__version__)" #Running a Python command directly
uv sync #Synchronizing the environment with the project lockfile
Now that our vehicle is ready for the journey, we can start our journey.
Pandas
Pandas is a Python library built for data manipulation and analysis. It provides convenient structures for working with labelled, tabular data.
Pandas is the data analyst's workstation. It helps you load data, inspect its structure, clean inconsistencies, filter records, combine datasets, calculate metrics, and prepare information for reporting.
Pandas has two important data structures:
- Series: which is a one-dimensional labelled array.
- DataFrame: a two-dimensional table with labelled rows and columns.
Importing Pandas
Note, we use the conventional alias pd
Confirming the environment variables
Creating and checking a Pandas Series
sales = pd.Series([1200, 1500, 1800, 1300, 2200])
print(sales)
Creating a DataFrame
data = {
"BookingID": ["BK-2023-00001", "BK-2023-00002", "BK-2023-00003"],
"BookingDate": ["2023-01-01", "2023-01-02", "2023-01-01"],
"CustomerID": ["C01696", "C01796", "C01546"]
}
df = pd.DataFrame(data)
print(df)
Inspecting your data before analysis (EDA)
Before any analysis, we must use clean data, but before knowing the data is clean, we must inspect it through exploratory data analysis.
Here are some of the commands we use to inspect our data with
print(df.head()) #To check the first 5 rows of our data
print(df.tail()) #To check the last 5 rows of the data
print(df.shape) # Checking number of rows and columns
print(df.columns) # checking column names
print(df.dtypes) # checking data types
print(df.info()) # checking the summary of our dataset
print(df.describe()) # checking the description of the dataset
Reading data from files
Most often than not, we will be using external data sources rather than manually creating dataframes.
Disclaimer: For large datasets, always consider reading only the necessary columns or processing the data in chunks rather than loading everything into memory at once.
For a CSV file:
df = pd.read_csv("sales.csv")
For an Excel workbook:
df = pd.read_excel("sales.xlsx")
For a JSON file:
df = pd.read_json("sales.json")
For a database,
df = pd.read_sql(
"SELECT * FROM sales",
con=engine #"engine" represents an already configured SQLAlchemy database engine. It must exist before the query can run.
)
Selecting rows and columns with loc and iloc
loc selects by label.
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet"],
"sales": [400000, 360000, 200000]
}, index=["A", "B", "C"])
print(df.loc["B"])
This returns the row labelled B.
You can also select a particular column:
print(df.loc["B", "sales"])
iloc selects by index position. - I like to call it index location
print(df.iloc[1])
This returns the second row because Python uses zero-based indexing.
To select the second row and second column:
print(df.iloc[1, 1])
Remember the difference:
-
locuses row and column labels. -
ilocuses integer positions.
Creating new columns
Suppose our sales dataset contains the number of units sold and the unit price, but not total revenue.
We can calculate it for every line in a new column by running:
df["revenue"] = df["units_sold"] * df["unit_price"]
print(df)
The new revenue column is calculated for every row.
You could also calculate a discount or profit margin:
df["discount_rate"] = 0.05
df["discounted_revenue"] = (
df["revenue"] * (1 - df["discount_rate"])
)
Filtering records using conditions
Management wants to know which transactions generated revenue above KES 300,000.
high_revenue = df[df["revenue"] > 300000]
print(high_revenue)
You can also apply multiple conditions:
nairobi_laptops = df[
(df["branch"] == "Nairobi") &
(df["product"] == "Laptop")
]
print(nairobi_laptops)
The parentheses are important when combining conditions with & and |.
For a condition that excludes a particular category:
non_laptop_sales = df[df["product"] != "Laptop"]
Cleaning missing values
Real-world datasets are rarely perfect. A missing value might indicate an unrecorded transaction, an unavailable measurement, or an incomplete form.
Identifying missing values:
print(df.isna())
print(df.isna().sum())
Remove rows containing any missing values:
clean_df = df.dropna()
Fill missing values with a known default:
df["units_sold"] = df["units_sold"].fillna(0)
You can also fill missing numerical values using a median:
median_units = df["units_sold"].median()
df["units_sold"] = df["units_sold"].fillna(median_units)
Please note that this can be appropriate in some analytical settings, but the decision should be guided by the meaning of the data and the analytical objective. Always we need to understand the business context for us to deal with missing values objectively and maintain the correctness and accuracy of our final product.
Removing duplicate records
Duplicate records can inflate revenue, transaction counts, and other metrics.
To identify duplicates:
print(df.duplicated().sum())
To remove fully duplicated rows:
df = df.drop_duplicates()
In production, first determine why the duplicates exist and whether one record contains more reliable information than another.For example, if a transaction ID uniquely identifies a transaction, use it to define duplicates:
df = df.drop_duplicates(
subset=["transaction_id"],
keep="first"
)
This assumes that the first record is the one you want to retain.
Correcting data types
Suppose revenue has been imported as text rather than a number.
df["revenue"] = pd.to_numeric(
df["revenue"],
errors="coerce"
)
The errors="coerce" argument converts values that cannot be interpreted as numbers to missing values.
For dates:
df["order_date"] = pd.to_datetime(
df["order_date"],
errors="coerce"
)
Date parsing is essential when doing time analysis. Pandas can parse dates while loading a CSV:
df = pd.read_csv(
"sales.csv",
parse_dates=["order_date"]
)
Grouping and aggregating data
To answer meaningful business questions, grouping and aggregation plays a big part of it as it uncovers trends and patterns by summarizing.
For us to know total revenue by branch, we can :
branch_revenue = (
df.groupby("branch")["revenue"]
.sum()
.reset_index()
)
print(branch_revenue)
To calculate average revenue by product:
product_performance = (
df.groupby("product")["revenue"]
.mean()
.reset_index()
)
And to calculate several metrics together:
summary = (
df.groupby("product")
.agg(
total_units=("units_sold", "sum"),
total_revenue=("revenue", "sum"),
average_revenue=("revenue", "mean")
)
.reset_index()
)
print(summary)
Sorting and ranking results
Let's say we want to know which products generate the most revenue.
ranked_products = summary.sort_values(
by="total_revenue",
ascending=False
)
print(ranked_products)
Setting the argument ascending=False sorts from highest to lowest.
We can also create a ranking column by:
summary["revenue_rank"] = (
summary["total_revenue"]
.rank(method="dense", ascending=False)
.astype(int)
)
This assigns rank 1 to the product with the highest revenue, with tied values receiving the same rank.
Combining datasets with merge
This is similar to Joins in SQL, Excel, or Power BI.
Suppose you have a transaction table and a separate table containing branch locations.
sales = pd.DataFrame({
"branch_id": [1, 2, 1, 3],
"revenue": [400000, 360000, 200000, 240000]
})
branches = pd.DataFrame({
"branch_id": [1, 2, 3],
"branch_name": ["Nairobi", "Mombasa", "Kisumu"]
})
To combine them using a left merge to retain all records from the sales dataset and add matching branch information where available, by running:
combined = sales.merge(
branches,
on="branch_id",
how="left"
)
print(combined)
We can use other common join types such as inner, right, and outer accordingly.
Exporting cleaned data
Once your analysis is complete, you may need to share the results with Excel users, a database, or another analytics tool.
Export to CSV:
df.to_csv("cleaned_sales.csv", index=False)
Export to Excel:
df.to_excel("cleaned_sales.xlsx", index=False)
The index=False argument prevents the DataFrame index from being written as an extra column.
Now let's look at how our numerical engine works.
NumPy
What is NumPy?
NumPy, which is Numerical Python in full, is a Python library for numerical computing. Its most common data structure is the multidimensional array, called the ndarray.
NumPy array is designed for efficient numerical computation and typically stores elements using a consistent data type.
NumPy is powerful when our data is just a sequence of numbers. When our data contains product names, dates, branches, categories, and missing values, that's where Pandas comes in.
So how is NumPy important for our Journey?
Say we have thousands of sales values. We want to calculate the total, average, standard deviation, or percentage change. We could write loops that process each value individually, which is kinda hectic. That's where NumPy comes in, providing optimised array operations that often perform such calculations more efficiently.
NumPy is especially useful for:
- Mathematical and statistical calculations.
- Working with vectors and matrices.
- Broadcasting operations across arrays.
- Random-number generation and simulations.
- Numerical operations used in scientific computing and machine learning.
Importing NumPy
import numpy as np #np is the conventional alias for NumPy
Creating NumPy arrays
Say we record daily sales for five days.
daily_sales = np.array([1200, 1500, 1800, 1300, 2200])
print(daily_sales)
Here are the ways to inspect an array.
print(daily_sales.ndim) # Number of dimensions (1D or 2D)
print(daily_sales.shape) # Shape of the array
print(daily_sales.size) # Number of elements
print(daily_sales.dtype) # Data type
Performing calculations without writing loops (Vectorization)
** Vectorization **: performing an operation over an array without explicitly writing a Python loop for every element.
Suppose every product's price increases by 10%. With NumPy, you can operate directly on the array without using loops.
prices = np.array([100, 200, 300, 400])
new_prices = prices * 1.10
print(new_prices)
A few NumPy mathematical operations
sales = np.array([1200, 1500, 1800, 1300, 2200])
print("Total:", np.sum(sales)) #total sales
print("Average:", np.mean(sales)) #average performance
print("Median:", np.median(sales)) #median sales value
print("Minimum:", np.min(sales)) #lowest sales value
print("Maximum:", np.max(sales)) #highest sales value
print("Standard deviation:", np.std(sales)) #How much did sales vary around the average
Filtering arrays with conditions
For example, to get Sales greater than 1,500.
sales = np.array([1200, 1500, 1800, 1300, 2200])
high_sales = sales[sales > 1500]
print(high_sales)
Multidimensional arrays
More often than not, real world numerical data has more than one dimension.
weekly_sales = np.array([
[1200, 1500, 1800],
[1300, 1700, 2100],
[1100, 1600, 1900]
])
print(weekly_sales)
print("Shape:", weekly_sales.shape)
print("Total:", np.sum(weekly_sales))
print("Column totals:", np.sum(weekly_sales, axis=0))
print("Row totals:", np.sum(weekly_sales, axis=1))
The array contains three rows and three columns.
Broadcasting
Broadcasting helps us to perform operations between compatible array shapes.
prices = np.array([100, 200, 300])
discounted_prices = prices - 20
print(discounted_prices)
The scalar 20 is broadcast across the array.
Broadcasting becomes even more powerful when dealing with arrays of different but compatible shapes.
prices = np.array([
[100, 200, 300],
[400, 500, 600]
])
tax_rates = np.array([0.16, 0.16, 0.16])
prices_with_tax = prices * (1 + tax_rates)
print(prices_with_tax)
The three tax rates align with the three columns, so the operation applies the relevant rate to each column in both rows.
Generating numerical sequences
NumPy also helps create numerical sequences. np.arange(start, stop, step) generates values from the starting point up to, but excluding, the stopping point.
numbers = np.arange(0, 10, 2)
print(numbers)
Output:
[0 2 4 6 8]
For a specified number of evenly spaced values:
values = np.linspace(0, 100, 5)
print(values)
Output:
[ 0. 25. 50. 75. 100.]
Unlike arange, linspace allows you to specify how many values you want.
Handling missing numerical values
NumPy includes NaN, which represents a floating-point missing or invalid numerical value.
measurements = np.array([10, 20, np.nan, 40, 50])
print(np.mean(measurements))
print(np.nanmean(measurements))
The mean returns NaN because a missing value is present.
Using np.nanmean() However, helps us ignore NaN values.
The Final Stretch Home
Tools Are Powerful, but thinking as a data analyst is the real skill. When we combine all these tools for our project, and an analytical and critical thinking mind. We become an unstoppable force in the world of analytics.
We should never focus on "Which Python function ot tool should I use?"
We should always to begin by asking:
- What problem am I trying to solve?
- What does each record represent?
- Is the data accurate and complete enough for the task?
- Which calculations are appropriate?
- What assumptions am I making?
- What can I responsibly conclude from the results?
That is where the real power of data begins, and this is the final destination of our journey today. Think like an analyst!
來源:DEV Community · dev.to








