利用 PyJanitor 高效清洗数据:从入门到精通的实战指南

在数据分析的日常工作中,我们常常面临一个尴尬的现实:尽管我们花费了大约 80% 的时间来清洗和准备数据,但这项工作往往是最枯燥、最容易出错的环节。你是否曾因为处理缺失值、重复的行名或不规范的列名而感到精疲力竭?虽然 Pandas 是 Python 数据生态的基石,但在处理一些繁琐的“脏活累活”时,代码往往会变得冗长且难以维护。

在这篇文章中,我们将深入探讨一个强大的开源库——PyJanitor。它不仅仅是对 Pandas 的简单封装,更是一种旨在提升数据清洗体验的思维方式。我们将通过一系列实际的代码示例,向你展示如何利用 PyJanitor 简化工作流,编写更易读、更健壮的数据清洗代码。无论你是数据分析的新手,还是寻求效率提升的资深开发者,这篇文章都将为你带来实用的见解。

为什么选择 PyJanitor?

PyJanitor 的核心理念深受 R 语言中 janitor 包的启发,其目标是解决 Pandas 在某些特定操作上的痛点。它并没有试图取代 Pandas,而是作为一个完美的补充,提供了更加直观、人性化的 API 设计。通过支持方法链,PyJanitor 允许我们将多个清洗步骤串联起来,形成一个清晰的数据处理管道。这不仅减少了中间变量的使用,还让代码的逻辑意图变得一目了然。

让我们来看看它的几个核心优势:

  • 方法链: 这是 PyJanitor 的灵魂。我们可以将清洗步骤像流水线一样串联,极大地提高了代码的可读性。
  • 现成的便捷函数: 针对删除空列、清洗列名、数据类型转换等高频场景,提供了开箱即用的解决方案。
  • 无缝集成: 它基于 Pandas 构建,直接扩展了 DataFrame 的功能,你不需要学习全新的数据结构。
  • 鲁棒性: 在处理脏数据时,PyJanitor 的函数往往包含了更多的边界情况处理,减少了代码出错的可能性。

准备工作:安装 PyJanitor

在开始探索之前,请确保你的环境中已经安装了该库。你可以直接使用 pip 进行安装:

pip install pyjanitor
``
安装完成后,我们就可以在代码中通过 `import janitor` 来启用它的所有功能了。值得注意的是,PyJanitor 会自动为 Pandas DataFrame 注册新的方法,所以我们可以像使用原生 Pandas 方法一样调用它。

---

### 核心功能与实战案例

接下来,让我们通过具体的例子,看看 PyJanitor 如何解决常见的数据清洗难题。为了让你更好地理解,我们不仅会展示代码,还会深入讲解背后的逻辑和实际应用场景。

#### 1. 标准化列名:告别混乱的命名规范

在处理从不同来源导出的数据集时,列名的不规范往往令人头疼。你可能见过 `‘First Name‘`、`‘first_name‘`、`‘FIRST NAME‘` 甚至 `‘   First-Name   ‘` 等各种变体。这种不一致性会严重干扰后续的数据分析。

`clean_names()` 函数就是为此而生的。它不仅能将所有字符转换为小写,还能智能处理空格和特殊字符,将其替换为下划线。这就好比给所有的列名做了一次彻底的大扫除。

python

import pandas as pd

import janitor

模拟一个列名非常混乱的 DataFrame

data = {

‘First Name‘: [1, 2, 3, 4],

‘Last Name‘: [5, 6, 7, 8],

‘Age (Years)‘: [9, 10, 11, 12],

‘ Income$ ‘: [100, 200, 300, 400] # 包含空格和特殊符号

}

df = pd.DataFrame(data)

使用 clean_names 进行清洗

remove_special=True 会移除特殊字符(如 $)

dfcleaned = df.cleannames(remove_special=True)

print("清洗后的列名:", df_cleaned.columns.tolist())

print(df_cleaned.head())


**输出结果:**

清洗后的列名: [‘firstname‘, ‘lastname‘, ‘ageyears‘, ‘income‘]

firstname lastname ageyears income

0 1 5 9 100

1 2 6 10 200

2 3 7 11 300

3 4 8 12 400


**实用见解:** 你可以看到,`‘Age (Years)‘` 变成了 `‘age_years_‘`(括号被移除,空格变下划线),而 `‘   Income$   ‘` 变成了 `‘income‘`。这种标准化的列名对于后续编写自动化脚本至关重要,因为你不再需要猜测列名的具体拼写格式。

#### 2. 剔除无效数据:移除空行与空列

在实际业务中,数据导出过程中往往会产生一些完全为空的行或列,这些数据不仅占用内存,还会干扰某些统计计算。虽然我们可以用 Pandas 的 `dropna` 来实现,但 PyJanitor 的 `remove_empty()` 提供了更加语义化的表达。

python

import pandas as pd

import janitor

创建包含空行和空列的数据

data = {

‘A‘: [1, None, 3],

‘B‘: [None, None, None], # 这是一列完全为空的数据

‘C‘: [4, 5, 6]

}

df = pd.DataFrame(data)

在索引1处添加一个完全为空的行

df.loc[3] = [None, None, None]

print("原始数据:")

print(df)

使用 remove_empty 移除全是空值的行和列

dfcleaned = df.removeempty()

print("

清洗后数据(移除了空列 B 和 空行):")

print(df_cleaned)


**输出结果:**

原始数据:

A B C

0 1.0 NaN 4.0

1 NaN NaN 5.0

2 3.0 NaN 6.0

3 NaN NaN NaN

清洗后数据(移除了空列 B 和 空行):

A C

0 1.0 4.0

1 NaN 5.0

2 3.0 6.0


**实用见解:** 这个函数特别适合作为数据加载后的第一步操作。它能帮你快速剔除那些没有任何信息的“垃圾数据”,让你的 DataFrame 变得更加紧凑和干净。

#### 3. 智能识别重复数据

数据重复是另一个常见问题,但在处理时我们往往需要更加谨慎。简单的 `drop_duplicates()` 会直接删除数据,而有时我们首先需要识别哪些数据是重复的,分析重复的原因。

PyJanitor 增强了这一领域的功能。虽然 Pandas 也有 `duplicated()`,但 PyJanitor 的设计理念鼓励我们在清洗管道中更加流畅地使用这些判断。

python

import pandas as pd

import janitor

data = {

‘ID‘: [101, 102, 102, 103],

‘Name‘: [‘Alice‘, ‘Bob‘, ‘Bob‘, ‘Charlie‘],

‘Value‘: [10, 20, 20, 30]

}

df = pd.DataFrame(data)

检查完全重复的行

keep=False 表示将所有重复项都标记为 True

df[‘is_duplicate‘] = df.duplicated(keep=False)

print("标记重复数据:")

print(df)


**输出结果:**

标记重复数据:

ID Name Value is_duplicate

0 101 Alice 10 False

1 102 Bob 20 True

2 102 Bob 20 True

3 103 Charlie 30 False


在这个例子中,我们可以清楚地看到索引 1 和 2 是完全重复的。在实际工作中,你可能会结合条件过滤,进一步分析这些重复数据是由于系统错误还是业务逻辑导致的。

#### 4. 优化内存使用:自动编码分类变量

如果你的数据集中包含大量重复的字符串(例如“性别”、“国家”、“等级”),使用 `object` 类型存储会占用大量内存。将这些列转换为 `category` 类型是 Pandas 中常见的优化手段。

PyJanitor 的 `encode_categorical()` 让这一过程变得声明式,特别是在链式操作中非常方便。它允许你明确指定哪些列需要被转换,从而让代码意图更加清晰。

python

import pandas as pd

import janitor

data = {

‘Customer_ID‘: [1, 2, 3, 4, 5],

‘Tier‘: [‘Gold‘, ‘Silver‘, ‘Gold‘, ‘Bronze‘, ‘Silver‘],

‘Country‘: [‘USA‘, ‘UK‘, ‘USA‘, ‘Canada‘, ‘UK‘]

}

df = pd.DataFrame(data)

print("转换前的数据类型:")

print(df.dtypes)

将 Tier 和 Country 列转换为 category 类型

dfencoded = df.encodecategorical(column_names=[‘Tier‘, ‘Country‘])

print("

转换后的数据类型:")

print(df_encoded.dtypes)

查看数据本身,内容没变,但内部存储结构变了

print("

数据预览:")

print(df_encoded.head())


**输出结果:**

转换前的数据类型:

Customer_ID int64

Tier object

Country object

dtype: object

转换后的数据类型:

Customer_ID int64

Tier category

Country category

dtype: object

数据预览:

Customer_ID Tier Country

0 1 Gold USA

1 2 Silver UK

2 3 Gold USA

3 4 Bronze Canada

4 5 Silver UK


**实用见解:** 对于拥有数百万行数据的大型数据集,这种转换可以将内存占用减少 50% 甚至更多。这是性能优化的第一步,PyJanitor 让它变得非常简单。

#### 5. 高级过滤:基于字符串条件的筛选

Pandas 的布尔索引虽然强大,但语法有时略显繁琐。PyJanitor 提供了 `filter_string` 方法,专门用于快速过滤包含特定字符串的行,非常适合进行日志分析或文本筛选。

python

import pandas as pd

import janitor

data = {

‘Product‘: [‘Apple iPhone‘, ‘Samsung Galaxy‘, ‘Google Pixel‘, ‘Apple iPad‘],

‘Price‘: [999, 899, 799, 699]

}

df = pd.DataFrame(data)

快速筛选出包含 ‘Apple‘ 的产品行

filtereddf = df.filterstring(columnname=‘Product‘, searchstring=‘Apple‘)

print("筛选出的苹果产品:")

print(filtered_df)


**输出结果:**

筛选出的苹果产品:

Product Price

0 Apple iPhone 999

3 Apple iPad 699


这个方法比 `df[df[‘Product‘].str.contains(‘Apple‘)]` 更符合自然语言习惯,也更容易记忆。

---

### 终极武器:链式操作

单独使用这些功能固然方便,但 PyJanitor 的真正威力在于**链式调用**。我们可以将上述所有步骤串联在一起,一次性完成从加载到清洗的全过程。这种写法被称为“流畅接口”,它让代码看起来像是在读一个故事。

让我们看一个综合实战案例:

python

import pandas as pd

import janitor

1. 模拟一个“脏”数据集

raw_data = {

‘ Employee Name ‘: [‘ Alice ‘, ‘ Bob ‘, ‘Charlie‘, ‘ Alice ‘],

‘ Department ‘: [‘HR‘, ‘IT‘, ‘IT‘, ‘HR‘],

‘ Salary (USD) ‘: [70000, 80000, None, 70000],

‘ Status ‘: [‘Active‘, ‘Active‘, ‘On Leave‘, ‘Active‘],

‘Useless Col‘: [None, None, None, None] # 全为空的列

}

dforiginal = pd.DataFrame(rawdata)

print("— 原始脏数据 —")

print(df_original)

2. 使用 PyJanitor 进行清洗管道操作

df_clean = (

df_original

# 步骤 1: 清洗列名(去除空格、转小写、移除特殊符号)

.cleannames(removespecial=True)

# 步骤 2: 移除完全为空的列

.remove_empty()

# 步骤 3: 移除完全重复的行(比如 Alice 的两行记录如果完全一样就删掉)

.remove_duplicates()

# 步骤 4: 将 Department 和 Status 转换为分类类型以节省内存

.encodecategorical(columnnames=[‘department‘, ‘status‘])

)

print("

— 清洗后的数据 —")

print(df_clean)

print("

— 数据类型信息 —")

print(df_clean.dtypes)


**输出结果:**

— 原始脏数据 —

Employee Name Department Salary (USD) Status Useless Col

0 Alice HR 70000.0 Active None

1 Bob IT 80000.0 Active None

2 Charlie IT NaN On Leave None

3 Alice HR 70000.0 Active None

— 清洗后的数据 —

employeename department salaryusd status

0 Alice HR 70000.0 Active

1 Bob IT 80000.0 Active

2 Charlie IT NaN On Leave

— 数据类型信息 —

employee_name object

department category

salary_usd float64

status category

dtype: object

“INLINECODE7f3399b2cleannames()INLINECODEc695f11a‘ Salary (USD) ‘INLINECODE766c560d‘salaryusd‘INLINECODEa525b0d2removeempty()INLINECODEae97dc46‘Useless Col‘INLINECODEcafec3b9removeduplicates()INLINECODE6838660cencodecategorical()INLINECODE66555100encodecategoricalINLINECODEcf7aba34cleannamesINLINECODE09970f07removeemptyINLINECODE2140dff3removeduplicatesINLINECODE89a662e9cleannames()` 开始,逐步感受它带来的便利。随着你对这些便捷函数的熟悉,你会发现原本枯燥的数据清洗工作竟然可以变得如此令人愉悦。

希望这篇指南能帮助你更快地搞定脏数据,把更多的时间花在真正有价值的数据分析和洞察上。如果你在实战中遇到了特殊的数据清洗难题,不妨查阅 PyJanitor 的官方文档,它的社区还在不断壮大,总会有适合你的工具。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/33597.html
点赞
0.00 平均评分 (0% 分数) - 0