
本教程旨在解决pandas数据处理中常见的挑战:如何将包含混合分隔符和文本(英文单词)表示数字的单列数据,拆分成多个独立的数值列。我们将探讨使用正则表达式提取数据、结合`word2number`库将文本数字转换为数值,并利用pandas的强大功能进行高效的数据清洗、类型转换与结构重塑,确保数据准确性和可用性。
在数据分析和机器学习项目中,原始数据往往不够规整。一个常见的问题是,单个列中可能包含由多种分隔符连接的多个逻辑值,并且这些值可能以文本(如“three hundred and two”)和数字(如“203.0”)的混合形式存在。本教程将指导您如何使用Python的Pandas库结合正则表达式和word2number库来高效地处理这类复杂的数据清洗任务。
假设我们有一个DataFrame,其中包含类似cement_water和coarse_fine_aggregate这样的列。 cement_water列的值可能形如three hundred and two;203.0或one hundred and fifty-one;184.4,甚至three hundred and sixty-two_164.9。这里存在以下挑战:
我们的目标是将这些列转换为以下结构:
| cement | water | coarse_aggregate | fine_aggregate |
|---|---|---|---|
| 302.0 | 203.0 | 974.0 | 817.0 |
| 151.0 | 184.4 | 992.0 | 815.9 |
| 362.0 | 164.9 | 944.7 | 755.8 |
许多用户在处理这类问题时,可能会尝试先统一分隔符,然后使用str.split()进行拆分,最后再对文本数字进行转换。
import pandas as pd
from word2number import w2n
# 假设df已加载
# df = pd.read_csv('your_data.csv')
# 模拟数据
data = {
'cement_water': ['three hundred and two;203.0', 'one hundred and fifty-one;184.4', 'three hundred and sixty-two_164.9'],
'coarse_fine_aggregate': ['974.0,817.0', '992.0;815.9', '944.7;755.8']
}
df = pd.DataFrame(data)
def convert_words_to_numbers(text):
# 尝试统一分隔符并分割,然后转换
# 这种方法存在问题,因为w2n.word_to_num不能处理纯数字字符串
words = text.replace('_', ' ').replace(';', ' ').replace(',', ' ').split()
converted_words = []
for word in words:
if word.isalpha(): # 如果是字母,尝试转换为数字
converted_words.append(str(w2n.word_to_num(word)))
else: # 否则直接添加
converted_words.append(word)
return ' '.join(converted_words)
# 应用转换函数
df['cement_water_processed'] = df['cement_water'].apply(lambda x: convert_words_to_numbers(x))
# 尝试拆分
# df[['cement', 'water']] = df['cement_water_processed'].str.split(' ', expand=True)
# 这段代码会因为w2n.word_to_num无法处理"203.0"等纯数字字符串而报错,
# 即使修改了convert_words_to_numbers函数,也可能因为分隔符处理不当导致问题。上述代码尝试通过统一分隔符后进行split,并使用word2number转换。然而,w2n.word_to_num()函数设计用于将英文单词转换为数字,例如"two hundred",但它无法处理已经是数字格式的字符串,如"203.0"。如果尝试将"203.0"传递给w2n.word_to_num(),就会抛出ValueError: No valid number words found!。这表明我们需要更精细的策略来区分和处理文本数字和纯数字字符串。
为了解决上述问题,我们可以采用以下更健壮的方法:
如果我们的数据模式相对固定,即一个文本数字后跟一个分隔符,再跟一个纯数字,可以使用如下正则表达式。
import pandas as pd
from word2number import w2n
# 模拟数据
data = {
'cement_water': ['three hundred and two;203.0', 'one hundred and fifty-one;184.4', 'three hundred and sixty-two_164.9'],
'coarse_fine_aggregate': ['974.0,817.0', '992.0;815.9', '944.7;755.8']
}
df = pd.DataFrame(data)
# 1. 使用正则表达式提取 cement_water 列
# (?P<cement>.*) 捕获分隔符前的所有内容作为 'cement'
# [;,_] 匹配任意一个分隔符
# (?P<water>\d+.?\d*)$ 捕获分隔符后的数字(整数或浮点数)作为 'water',并确保在行尾
tmp_cement_water = df['cement_water'].str.extract(r'(?P<cement>.*)[;,_](?P<water>\d+.?\d*)$')
# 2. 对 coarse_fine_aggregate 列进行拆分
# [;,_] 匹配任意一个分隔符进行拆分
tmp_coarse_fine = df['coarse_fine_aggregate'].str.split('[;,_]', expand=True) \
.rename(columns={0: 'coarse_aggregate', 1: 'fine_aggregate'})
# 3. 将 'cement' 列的文本数字转换为数值
# 使用 .map() 配合 w2n.word_to_num 对 'cement' 列进行转换
tmp_cement_water['cement'] = tmp_cement_water['cement'].map(w2n.word_to_num)
# 4. 合并所有结果列并转换为浮点型
out = pd.concat([tmp_cement_water, tmp_coarse_fine], axis=1).astype(float)
print("方案一结果:")
print(out)输出示例:
cement water coarse_aggregate fine_aggregate 0 302.0 203.0 974.0 817.0 1 151.0 184.4 992.0 815.9 2 362.0 164.9 944.7 755.8
这个方案的关键在于正则表达式r'(?P<cement>.*)[;,_](?P<water>\d+.?\d*)$',它能够将文本数字部分(cement)与纯数字部分(water)有效分离。这样,w2n.word_to_num只作用于它能够处理的文本数字。
在某些情况下,cement部分可能也混合了纯数字字符串(尽管本例中不是),或者数据模式更复杂。为了提高鲁棒性,我们可以采用一个更通用的方法,即先尝试将列转换为数字,失败后再使用word2number。
import pandas as pd
from word2number import w2n
# 模拟数据
data = {
'cement_water': ['three hundred and two;203.0', 'one hundred and fifty-one;184.4', 'three hundred and sixty-two_164.9'],
'coarse_fine_aggregate': ['974.0,817.0', '992.0;815.9', '944.7;755.8']
}
df = pd.DataFrame(data)
# 1. 使用正则表达式提取 cement_water 列
tmp = df['cement_water'].str.extract(r'(?P<cement>.*)[;,_](?P<water>\d+.?\d*)$')
# 2. 尝试将 'cement' 列直接转换为数值,无法转换的标记为 NaN
s = pd.to_numeric(tmp['cement'], errors='coerce')
# 3. 找出那些无法转换为数值(即为 NaN)且原始数据不为空的行
m = s.isna() & df['cement_water'].notna()
# 4. 仅对这些无法直接转换为数值的行,使用 w2n.word_to_num 进行转换
# 注意:这里假设tmp['cement']中m对应的元素是word string,如果不是,w2n.word_to_num会报错
# 实际上,由于前面的regex已经将纯数字部分提取给了'water',这里'cement'通常都是word string
tmp.loc[m, 'cement'] = df.loc[m, 'cement_water'].map(lambda x: w2n.word_to_num(x.split('[;,_]')[0]) if isinstance(x, str) else x)
# 修正:更准确的做法是使用tmp['cement']而不是df['cement_water'],因为tmp['cement']已经包含了提取出的word string
tmp.loc[m, 'cement'] = tmp.loc[m, 'cement'].map(w2n.word_to_num)
# 5. 对 coarse_fine_aggregate 列进行拆分
tmp_coarse_fine = df['coarse_fine_aggregate'].str.split('[;,_]', expand=True) \
.rename(columns={0: 'coarse_aggregate', 1: 'fine_aggregate'})
# 6. 合并所有结果列并转换为浮点型
out = pd.concat([tmp, tmp_coarse_fine], axis=1).astype(float)
print("\n方案二结果:")
print(out)输出示例:
cement water coarse_aggregate fine_aggregate 0 302.0 203.0 974.0 817.0 1 151.0 184.4 992.0 815.9 2 362.0 164.9 944.7 755.8
方案二的优势在于其灵活性。它首先尝试最直接的数值转换,只有当转换失败时(说明是文本数字),才调用word2number。这避免了不必要的word2number调用,并能处理更复杂的数据混合情况。
通过本教程,您应该能够熟练地处理Pandas中包含混合分隔符和文本数字的复杂列拆分与转换任务,从而为后续的数据分析奠定坚实的基础。
以上就是Pandas数据清洗:高效处理混合分隔符与文本数字的列拆分与转换的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号