
本教程旨在解决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。这里存在以下挑战:
混合分隔符: 值之间可能使用;、,或_等不同字符进行分隔。文本数字: 部分数值以英文单词形式(如three hundred and two)存在,需要转换为实际的数字。类型转换: 最终所有拆分出的列都应为数值类型。
我们的目标是将这些列转换为以下结构:
302.0203.0974.0817.0151.0184.4992.0815.9362.0164.9944.7755.8
初始尝试与遇到的问题
许多用户在处理这类问题时,可能会尝试先统一分隔符,然后使用str.split()进行拆分,最后再对文本数字进行转换。
import pandas as pdfrom 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!。这表明我们需要更精细的策略来区分和处理文本数字和纯数字字符串。
解决方案:结合正则表达式与条件转换
为了解决上述问题,我们可以采用以下更健壮的方法:
使用正则表达式精确提取: 利用str.extract()结合正则表达式,可以同时处理多种分隔符,并区分文本部分和数字部分。有条件地应用word2number: 仅对那些确实是文本数字的部分应用w2n.word_to_num()。统一数据类型: 最终将所有目标列转换为浮点数。
方案一:针对特定模式的直接提取与转换
如果我们的数据模式相对固定,即一个文本数字后跟一个分隔符,再跟一个纯数字,可以使用如下正则表达式。
import pandas as pdfrom 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'# [;,_] 匹配任意一个分隔符# (?Pd+.?d*)$ 捕获分隔符后的数字(整数或浮点数)作为 'water',并确保在行尾tmp_cement_water = df['cement_water'].str.extract(r'(?P.*)[;,_](?Pd+.?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_aggregate0 302.0 203.0 974.0 817.01 151.0 184.4 992.0 815.92 362.0 164.9 944.7 755.8
这个方案的关键在于正则表达式r'(?P.*)[;,_](?Pd+.?d*)$’,它能够将文本数字部分(cement)与纯数字部分(water)有效分离。这样,w2n.word_to_num只作用于它能够处理的文本数字。
方案二:更通用的条件转换(推荐)
在某些情况下,cement部分可能也混合了纯数字字符串(尽管本例中不是),或者数据模式更复杂。为了提高鲁棒性,我们可以采用一个更通用的方法,即先尝试将列转换为数字,失败后再使用word2number。
import pandas as pdfrom 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.*)[;,_](?Pd+.?d*)$')# 2. 尝试将 'cement' 列直接转换为数值,无法转换的标记为 NaNs = 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 stringtmp.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 stringtmp.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_aggregate0 302.0 203.0 974.0 817.01 151.0 184.4 992.0 815.92 362.0 164.9 944.7 755.8
方案二的优势在于其灵活性。它首先尝试最直接的数值转换,只有当转换失败时(说明是文本数字),才调用word2number。这避免了不必要的word2number调用,并能处理更复杂的数据混合情况。
关键注意事项与总结
正则表达式的精准性: 正则表达式是此解决方案的核心。r'(?Ppattern)’语法允许您命名捕获组,这使得后续处理更加方便。根据您的数据模式,可能需要调整正则表达式以精确匹配。例如,[;,_]可以匹配分号、逗号或下划线中的任意一个作为分隔符。d+.?d*用于匹配整数或浮点数。word2number库: 确保已安装word2number (pip install word2number)。它对于处理英文文本数字非常有效,但请记住它不能处理纯数字字符串。pd.to_numeric(errors=’coerce’): 这是一个强大的工具,可以尝试将Series转换为数值类型。当遇到无法转换的值时,errors=’coerce’参数会将其替换为NaN,这使得我们可以方便地识别并进一步处理这些异常值。数据类型转换: 最终使用.astype(float)确保所有新生成的列都是浮点数类型,这对于后续的数值计算和分析至关重要。性能考虑: 对于非常大的数据集,链式操作和多次apply可能会影响性能。上述解决方案中,通过矢量化的Pandas操作(如str.extract, str.split, map, pd.to_numeric, loc)可以获得较好的性能。错误处理: 在实际应用中,如果word2number遇到它无法识别的文本(例如拼写错误或非数字文本),仍然可能抛出错误。您可能需要添加额外的错误捕获机制,例如在map函数中使用try-except块。
通过本教程,您应该能够熟练地处理Pandas中包含混合分隔符和文本数字的复杂列拆分与转换任务,从而为后续的数据分析奠定坚实的基础。
以上就是Pandas数据清洗:高效处理混合分隔符与文本数字的列拆分与转换的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1379292.html
微信扫一扫
支付宝扫一扫