说起数据分析,很多人第一反应就是Excel。表格拉一拉,透视表一插,好像也挺厉害?但如果你真的想在数据分析这条路上走远,甚至想靠它在职场里升职加薪,那必须得把 Python 的 pandas 和 numpy 这对“黄金搭档”玩明白了。
别被这两个名字吓到。它们其实没那么神秘,只是把Excel里那些繁琐的手动操作,变成了几行代码,而且跑起来快得多。今天咱们就聊聊,怎么从“只会复制粘贴”变成“代码流大神”。
一、为什么是 numpy 和 pandas?
在 Python 的数据分析世界里,numpy 是地基,pandas 是盖在上面的高楼。
numpy 负责处理最基础的数值计算。它的数据结构叫 ndarray,可以理解为一个超级高效的数组。为什么高效?因为它在底层是用 C 语言写的,专门针对数学运算做了优化。你让 Excel 处理 100 万行数据的加减乘除,可能会卡死;而 numpy 毫秒级搞定。
pandas 则是在 numpy 的基础上,造出了两个核心工具:DataFrame 和 Series。你可以把 DataFrame 想象成一个加强版的 Excel 表格——它有行、有列、有标题,还能做各种复杂的筛选、合并、统计。
很多初学者会跳过 numpy 直接学 pandas,这没问题,但如果你想知道 pandas 底层是怎么跑得这么快的,或者想处理纯数值矩阵,numpy 的基本功必须扎实。
二、numpy 进阶:不只是加法乘法
numpy 入门谁都会,import numpy as np,然后 np.array([1, 2, 3])。但职场中遇到的数据,从来不是整齐的二维表格,而是各种形状的张量、掩码、广播机制。
1. 广播机制(Broadcasting):隐形的效率神器
这是 numpy 最强大的特性之一。简单来说,就是当两个形状不同的数组进行运算时,numpy 会自动“扩展”较小的数组,让它们形状匹配,而不需要你真的去复制数据。
比如,你有一个 3x3 的矩阵,想给每一行都减去一个均值向量,你不需要写循环,直接减就行:
import numpy as np
# 创建一个 3x3 的随机矩阵,模拟某公司三个部门三个月的销售额
sales = np.array([
[100, 150, 200],
[120, 180, 240],
[90, 130, 170]
])
# 计算每行的均值
row_means = sales.mean(axis=1)
print("行均值:", row_means) # 输出: [150. 180. 130.]
# 关键来了:sales 是 (3,3),row_means 是 (3,)
# numpy 会自动把 row_means 广播成 (3,3),然后逐元素相减
normalized_sales = sales - row_means
print("去均值后的销售额:\n", normalized_sales)
输出结果里,第一行每个数都减了 150,第二行都减了 180。不用写 for 循环,代码简洁,运行速度也快。在职场中,处理大规模数据时,避免循环,多用广播和向量化操作,是基本的素养。
2. 高级索引与布尔掩码
有时候,你不需要整个数组,只需要满足某个条件的部分。numpy 的布尔索引能让你像写自然语言一样筛选数据。
假设你有一组员工绩效分数,你想找出所有高于平均分且分数为偶数的员工 ID:
scores = np.array([88, 92, 75, 88, 96, 81, 90, 85])
indices = np.arange(len(scores))
# 创建布尔掩码
above_avg = scores > scores.mean() # 高于平均分
is_even = scores % 2 == 0 # 偶数
# 组合条件:两个掩码取交集
mask = above_avg & is_even
# 获取符合条件的索引和分数
qualified_employees = indices[mask]
qualified_scores = scores[mask]
print(f"符合条件的员工 ID: {qualified_employees}")
print(f"对应的分数: {qualified_scores}")
这种写法比传统的 if 判断循环清晰太多了,而且一旦数据量变大,性能优势非常明显。
三、pandas 进阶:从“会查表”到“会讲故事”
pandas 才是你日常工作中用得最多的工具。它的 API 设计非常人性化,几乎让你感觉不到自己在写代码,而是在“操作”一个智能表格。
1. 数据读取与清洗:别小看这几行
很多数据分析师 80% 的时间都在清洗数据。pandas 的 read_csv 只是开始,真正的功夫在参数里。
比如,你手头有一份日志文件,时间格式乱成一锅粥,列名还带空格,甚至有缺失值。你可以这样优雅地处理:
import pandas as pd
# 读取时直接指定列名,并跳过混乱的表头行
df = pd.read_csv('user_logs.csv', header=1, names=['user_id', 'event_type', 'timestamp', 'value'])
# 解析时间戳,指定格式,遇到无法解析的强制报错以便检查
df['timestamp'] = pd.to_datetime(df['timestamp'], format='%Y-%m-%d %H:%M:%S', errors='raise')
# 填充缺失值:数值列用中位数(避免极端值影响),字符串列用“未知”
df['value'] = df['value'].fillna(df['value'].median())
df['event_type'] = df['event_type'].fillna('unknown')
# 去除重复记录
df.drop_duplicates(inplace=True)
注意 errors='raise' 这个细节。在职场中,数据质量往往参差不齐,强制报错比默默填充 NaT 或 NaN 更安全,能让你第一时间发现数据源的问题。
2. GroupBy 的高级用法:不只是分组求和
groupby 是 pandas 的灵魂。但很多人只用到 .sum() 或 .mean(),这太浪费了。你可以对不同的列应用不同的聚合函数,这就是 agg 的魔力。
假设你要分析某电商平台的数据,想知道每个城市、每个品类的平均订单金额、最高单笔金额和订单总数:
# 模拟数据
data = {
'city': ['北京', '北京', '上海', '上海', '广州', '广州'],
'category': ['电子产品', '服装', '电子产品', '服装', '电子产品', '服装'],
'order_amount': [5000, 200, 6000, 150, 4500, 300]
}
df = pd.DataFrame(data)
# 多列聚合
summary = df.groupby(['city', 'category']).agg(
avg_amount=('order_amount', 'mean'), # 平均金额
max_amount=('order_amount', 'max'), # 最高单笔
order_count=('order_amount', 'count') # 订单数
).reset_index()
print(summary)
输出会是一个清晰的汇总表,直接可以拿去给老板做 PPT 了。reset_index() 这一步很重要,它把分组后的索引变回普通列,方便后续操作或导出。
3. 时间序列分析:升职加薪的隐藏技能
很多公司都有时序数据——股价、销量、用户活跃度。pandas 的时间序列功能非常强大,能帮你轻松处理重采样、滚动窗口等复杂需求。
比如,你有一份按小时记录的传感器数据,但老板只需要看“每天的最高温度”:
# 创建带时间索引的数据
dates = pd.date_range(start='2023-01-01', periods=48, freq='H') # 48小时,每小时一条
temp_data = pd.Series(np.random.randn(48) + 20, index=dates) # 模拟温度,基准20度
# 重采样为每天(D),并取最大值
daily_max = temp_data.resample('D').max()
print(daily_max.head())
再比如,你想计算“过去 7 天的滚动平均销量”,以此来判断趋势:
# 模拟每日销量
daily_sales = pd.Series(np.random.randint(100, 500, size=30),
index=pd.date_range('2023-01-01', periods=30, freq='D'))
# 7天滚动平均
rolling_avg = daily_sales.rolling(window=7).mean()
# 注意:前6天因为没有足够的数据点,结果是 NaN,这是正常的
# 用 fillna(0) 或者 ffill() 可以根据业务需求处理
这种时间序列的操作,在金融、零售、物联网领域是刚需。如果你能在面试或工作中熟练运用,面试官会眼前一亮。
4. 合并与连接:像 SQL 一样操作
职场中的数据结构往往是分散的。用户表在一个文件,订单表在另一个文件。pandas 提供了 merge 和 join,让你像写 SQL 一样合并数据。
users = pd.DataFrame({
'user_id': [1, 2, 3],
'user_name': ['Alice', 'Bob', 'Charlie']
})
orders = pd.DataFrame({
'order_id': [101, 102, 103],
'user_id': [1, 1, 2],
'amount': [100, 200, 150]
})
# 内连接:只保留两边都能匹配上的记录
merged_df = pd.merge(users, orders, on='user_id', how='inner')
# 左连接:保留所有用户,即使他们没有订单
left_merged = pd.merge(users, orders, on='user_id', how='left')
how 参数支持 inner、left、right、outer,这和 SQL 完全一致,上手几乎没有门槛。
四、实战案例:从数据到洞察
光说不练假把式。咱们来一个完整的迷你项目,模拟一个真实的职场场景。
场景:你是某电商公司的数据分析师,老板让你分析“为什么最近销售额下降”,并给出可视化建议。
步骤 1:加载数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 假设这是从数据库导出的最近30天的销售数据
df = pd.read_csv('sales_last_30_days.csv')
# 快速查看数据概览
print(df.info())
print(df.describe())
步骤 2:数据清洗
# 检查缺失值
print(df.isnull().sum())
# 删除关键列缺失的行
df.dropna(subset=['transaction_id', 'amount'], inplace=True)
# 转换日期格式
df['date'] = pd.to_datetime(df['date'])
步骤 3:探索性分析
# 按天聚合销售额
daily_sales = df.groupby('date')['amount'].sum().reset_index()
# 计算环比变化
daily_sales['previous_day_sales'] = daily_sales['amount'].shift(1)
daily_sales['change_pct'] = (daily_sales['amount'] - daily_sales['previous_day_sales']) / daily_sales['previous_day_sales'] * 100
# 找出下降最严重的几天
worst_days = daily_sales[daily_sales['change_pct'] < -10].sort_values('change_pct')
print("销售额下降超过10%的日期:\n", worst_days)
步骤 4:可视化呈现
plt.figure(figsize=(12, 6))
plt.plot(daily_sales['date'], daily_sales['amount'], label='Daily Sales', color='blue')
plt.fill_between(daily_sales['date'], daily_sales['amount'], alpha=0.3)
# 标记下降严重的点
for idx, row in worst_days.iterrows():
plt.annotate(f"降 {row['change_pct']:.1f}%",
xy=(row['date'], row['amount']),
xytext=(10, 10), textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='red'),
color='red')
plt.title('Sales Trend over Last 30 Days')
plt.xlabel('Date')
plt.ylabel('Sales Amount')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
这个过程,就是你日常工作的缩影。从脏数据到干净表格,从统计数字到可视化洞察,pandas 和 numpy 全程陪伴。
五、职场进阶小贴士
- 不要重复造轮子:遇到复杂操作,先想想 pandas 有没有现成的函数。比如,你写了一大段循环去去重,其实
drop_duplicates()一行就搞定了。 - 善用
apply但要谨慎:apply很灵活,可以套用自定义函数,但它本质上是循环,速度比向量化操作慢。大数据集下,尽量用内置函数或 numpy 向量化。 - 内存优化:当数据量大到内存爆掉时,可以转换数据类型,比如把
float64改成float32,把object字符串列改成category类型,能省下一半内存。 - 保持好奇心:数据分析的工具链在快速进化,polars、duckdb 等新库也在涌现。但无论怎么变,numpy 和 pandas 的核心思维——向量化、分组聚合、时间序列处理——是通用的。
掌握 pandas 和 numpy,不仅仅是学会两个库,更是学会了一种“用代码思考数据”的方式。当你不再被繁琐的手动操作束缚,能够用几行代码快速验证假设、挖掘洞察时,你就已经具备了数据分析师的核心竞争力。
升职加薪,从来不是靠加班堆出来的,而是靠你解决复杂问题的能力。而能力,就藏在这一行行代码里。加油!
