搜 索

pandas从入门到放弃

  • 348阅读
  • 2022年05月29日
  • 0评论
首页 / AI/大数据 / 正文

什么是Pandas

在Python的江湖中,如果说NumPy是内功心法,那Pandas就是降龙十八掌——简单粗暴,一招制敌。

不管你是做数据分析的表哥表姐,还是搞深度学习的炼丹师,Pandas都是你绑定的新手装备。它底层用C实现(所以别问为什么快,问就是C的功劳),提供了一堆让你"少掉头发"的数据处理API。

简单来说:没有Pandas,你的数据处理代码会像意大利面一样——又长又乱还容易断。


Pandas的两大数据结构

Pandas的核心就两个数据结构,记住它们,你就掌握了Pandas的半壁江山:

数据结构一句话解释类比
Series带标签的一维数组Excel的一列
DataFrame带标签的二维表格Excel的整张表
import pandas as pd
from pandas import Series, DataFrame

# Series: 就是一列数据
s = Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
print(s)
# a    1
# b    2
# c    3
# d    4

# DataFrame: 就是一张表
df = DataFrame({
    'name': ['张三', '李四', '王五'],
    'age': [25, 30, 35],
    'salary': [10000, 20000, 30000]
})
print(df)
#   name  age  salary
# 0   张三   25   10000
# 1   李四   30   20000
# 2   王五   35   30000
💡 记忆技巧:Series是"系列",DataFrame是"数据框"。一个是一维的,一个是二维的。就这么简单,别想复杂了。

Pandas的奇技淫巧

这部分是重点,建议反复观看,考试要考。

1. 删除异常数据

现实世界的数据就像你的代码——总有bug。空值、重复值、异常值,三座大山压在每个数据工程师头上。

# 删除包含空值的行(简单粗暴)
df.dropna()

# 只删除某些列为空的行(精准打击)
df.dropna(subset=['name', 'age'])

# 删除重复行
df.drop_duplicates()

# 按某些列去重(保留最后一条)
df.drop_duplicates(['card_bin', 'card_len'], keep='last')

# 条件删除:删除年龄小于0的异常数据(除非你在统计穿越者)
df = df[df['age'] >= 0]

# 组合条件删除:删除国家为AE且银行名为空的数据
df = df[~((df['cc'] == 'AE') & (df['bank_name'].isna()))]
# 或者用query,更优雅
df = df.query("not (cc == 'AE' and bank_name != bank_name)")

2. 补全缺失数据

有时候删除太暴力,我们需要温柔地填补空白。

# 用固定值填充
df['currency'].fillna('16', inplace=True)

# 用均值填充(适合数值型)
df['age'].fillna(df['age'].mean(), inplace=True)

# 用前一个值填充(适合时间序列)
df['price'].fillna(method='ffill', inplace=True)

# 用后一个值填充
df['price'].fillna(method='bfill', inplace=True)

# 不同列用不同策略填充(骚操作)
df.fillna({
    'name': '佚名',
    'age': df['age'].median(),
    'salary': 0
}, inplace=True)

3. 数据类型转换

Pandas读进来的数据类型经常不是你想要的,需要手动调教。

# 转换单列
df['age'] = df['age'].astype(int)

# 转换多列
df = df.astype({'age': int, 'salary': float})

# 字符串转日期(常用!)
df['date'] = pd.to_datetime(df['date'])

# 处理转换失败的情况(errors='coerce'会把失败的转成NaN)
df['amount'] = pd.to_numeric(df['amount'], errors='coerce')

4. 数据筛选的N种姿势

# 基础筛选
df[df['age'] > 25]

# 多条件筛选(注意括号!)
df[(df['age'] > 25) & (df['salary'] > 15000)]

# isin筛选(适合枚举值)
df[df['country'].isin(['CN', 'US', 'UK'])]

# query方法(写起来更像SQL)
df.query("age > 25 and salary > 15000")

# loc和iloc的区别
df.loc[0:5, ['name', 'age']]   # 按标签,包含结束位置
df.iloc[0:5, [0, 1]]           # 按位置,不包含结束位置

Pandas的相关库

Pandas不是一个人在战斗,它有一群好基友:

NumPy —— 矩阵计算扛把子

Pandas的底层就是NumPy数组。如果你要做矩阵运算、线性代数,NumPy是你的不二之选。

import numpy as np

# Pandas和NumPy无缝衔接
df['age'].values  # 返回NumPy数组
np.mean(df['age'])  # NumPy函数直接用

Matplotlib / Seaborn —— 可视化双雄

数据分析不画图,等于没分析。

import matplotlib.pyplot as plt

# Pandas内置绑定了Matplotlib
df['salary'].plot(kind='hist')
df.plot(x='age', y='salary', kind='scatter')
plt.show()

Scikit-learn —— 机器学习老大哥

当你用Pandas处理完数据,下一步就是喂给模型。Scikit-learn和Pandas配合得天衣无缝。


Pandas实战:卡BIN数据分析

理论说一万遍,不如实战来一遍。

背景

卡BIN是支付行业的重要数据,通过卡号前6-8位可以识别发卡行、卡组织、国家等信息。这类数据动辄几十万条,手写循环分析?等你跑完黄花菜都凉了。

样例数据(分号分隔):

457186;VISA/DANKORT;NETS;DEBIT;CLASSIC;DENMARK;DK;DNK;208;HTTP://WWW.TELLER.COM/;915 08 989;16;PERSONAL;N

字段说明

序号字段名说明特殊规则
1card_bin卡BIN-
2card_brand卡组织-
3bank_name银行名若国家为AE,银行名为空则舍弃;其他国家银行名为空保留
4drcr借记/贷记-
5class卡级别-
6country_name国家名-
7cc国家二字码-
8cc3国家三字码-
9currency币种若为空,默认值16
10website网站-
11phone_no银行电话-
12card_len卡号长度-
13person个人/公司-
14unknown未知-

实战代码

Step 1: 读取数据并抽样

import pandas as pd

file_path = "/opt/file/dataset/bins_all.csv"

# 读取CSV数据
# sep=';' 指定分隔符
# header=None 原文件没有表头
# dtype=str 所有字段按字符串读取(避免卡BIN被当成数字丢失前导零)
df = pd.read_csv(
    file_path, 
    sep=';', 
    header=None, 
    index_col=False,
    names=['card_bin', 'card_brand', 'bank_name', 'drcr', 'class', 
           'country_name', 'cc', 'cc3', 'currency', 'website', 
           'phone_no', 'card_len', 'person', 'unknown'],
    dtype=str
)

# 看看数据长啥样
print(f"总数据量: {len(df)}")
df.head()

# 随机抽样5000条用于分析(大数据集先抽样是好习惯)
df_sample = df.sample(5000)
df_sample.to_csv("/opt/file/dataset/bins_analysis.csv", index=False)

Step 2: 数据清洗

# 1. 删除重复数据(card_bin + card_len 确定唯一记录)
df_clean = df.drop_duplicates(['card_bin', 'card_len'], keep='last')
print(f"去重后: {len(df_clean)} 条")

# 2. 处理AE国家的特殊规则:cc为AE时,bank_name不能为空
#    翻译成人话:删除 (cc == 'AE') & (bank_name为空) 的行
df_clean = df_clean[~((df_clean['cc'] == 'AE') & (df_clean['bank_name'].isna()))]
# 或者用这种写法,更直观
# df_clean = df_clean.query("not (cc == 'AE' and bank_name != bank_name)")
print(f"处理AE规则后: {len(df_clean)} 条")

# 3. 填充currency的默认值
df_clean['currency'] = df_clean['currency'].fillna('16')

# 4. 查看清洗结果
print(df_clean.info())
print(df_clean.describe())

Step 3: 数据分析

# 按卡组织统计
print("=== 卡组织分布 ===")
print(df_clean['card_brand'].value_counts().head(10))

# 按国家统计
print("\n=== 国家分布 TOP 10 ===")
print(df_clean['cc'].value_counts().head(10))

# 交叉统计:各国家的卡组织分布
print("\n=== 各国卡组织分布 ===")
cross_tab = pd.crosstab(df_clean['cc'], df_clean['card_brand'])
print(cross_tab.head(10))

# 分组聚合:各卡组织的借记/贷记比例
print("\n=== 各卡组织借贷比例 ===")
print(df_clean.groupby(['card_brand', 'drcr']).size().unstack(fill_value=0))

Pandas在Java中的替代品

Python不是万能的,有时候你不得不用Java(比如你的老板要求,比如你的项目是Java栈)。

Java中模仿Pandas的库主要有两个:

库名特点成熟度
Joinery主要实现DataFrame,API风格接近Pandas⭐⭐
Tablesaw侧重数据可视化,图表功能强⭐⭐⭐
⚠️ 忠告:这两个库的知名度和成熟度都远逊于Pandas。如果可以,请坚定地选择Python。

Joinery vs Pandas 性能对比

测试环境:110MB CSV文件

测试场景Joinery (秒)Pandas (秒)结论
删除无效和重复数据411586Joinery略胜
100次全量读取并describe603270Pandas快一倍
50次读取并value_counts411174Pandas快一倍

结论

  • Joinery在简单的数据清洗场景下还行
  • 但涉及统计分析(describe、value_counts),Pandas完胜
  • Joinery的describe还缺少25%/50%/75%分位数,功能不全

建议:除非万不得已,否则别用Java做数据分析。真的,相信我。


常见坑点 & 避坑指南

坑1:链式赋值警告

# ❌ 错误写法(会有SettingWithCopyWarning)
df[df['age'] > 25]['salary'] = 0

# ✅ 正确写法
df.loc[df['age'] > 25, 'salary'] = 0

坑2:inplace参数的迷惑行为

# inplace=True 不返回值,直接修改原DataFrame
df.drop_duplicates(inplace=True)  # df被修改,返回None

# inplace=False(默认)返回新DataFrame,原DataFrame不变
df_new = df.drop_duplicates()  # df不变,df_new是新的

坑3:读取大文件内存爆炸

# 分块读取大文件
chunks = pd.read_csv('huge_file.csv', chunksize=100000)
for chunk in chunks:
    process(chunk)  # 分块处理

# 或者只读取需要的列
df = pd.read_csv('huge_file.csv', usecols=['col1', 'col2'])

坑4:日期解析的玄学

# 让Pandas自动解析日期
df = pd.read_csv('data.csv', parse_dates=['date_column'])

# 指定日期格式(更快更准)
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

总结

Pandas就像一把瑞士军刀:

  • 功能强大,但你可能只用到10%
  • 入门简单,精通需要时间
  • 遇到问题,先Google/Stack Overflow

从入门到放弃?不,是从入门到真香!


参考资料


如果这篇文章对你有帮助,请给作者点个赞。如果没有帮助……那一定是你打开的方式不对。 😏

评论区
暂无评论
avatar