我有一个通过从 Excel 电子表格导入创建的 Pandas 数据框。使用
.dtypes
方法时,该列的数据类型为 object
。然而,在单列内,存在str
、int
、float
等多种类型的数据。也可能存在缺失值 (NaN
)。一些字符串值实际上是整数值的字符串表示形式,具体来说,在本例中是 4 位年份。我希望能够将由 4 位数字组成的字符串值重新转换为整数,但保持所有其他值(和数据类型)不变。
举个例子,最小的数据框可能看起来像:
import numpy as np
import pandas as pd
import re
testdf = pd.DataFrame({'col1':['abc','2023',456,789,'2021',4.5,'123',np.nan]})
col1
0 abc
1 2023
2 456
3 789
4 2021
5 4.5
6 123
7 NaN
与数据类型:
col1 object
dtype: object
但是,各个单元格中的数据类型有所不同:
testdf['col1_types'] = testdf['col1'].apply(type)
col1 col1_types
0 abc <class 'str'>
1 2023 <class 'str'>
2 456 <class 'int'>
3 789 <class 'int'>
4 2021 <class 'str'>
5 4.5 <class 'float'>
6 123 <class 'str'>
7 NaN <class 'float'>
我提出的解决方案涉及如下所示的几个步骤(为了清楚起见,将各个步骤的结果添加为数据框中的新列),但该过程似乎非常笨重。直觉上,我认为应该有一种更简单的方法来做到这一点 - 甚至可能作为单行 - 但我还没有能够解决语法问题。我使用的步骤如下:
第 1 步 - 创建一个掩码,指示哪些单元格包含字符串
strmask = testdf['col1'].apply(type) == str
testdf['strmask'] = strmask
col1 col1_types strmask
0 abc <class 'str'> True
1 2023 <class 'str'> True
2 456 <class 'int'> False
3 789 <class 'int'> False
4 2021 <class 'str'> True
5 4.5 <class 'float'> False
6 123 <class 'str'> True
7 NaN <class 'float'> False
第 2 步 - 测试包含字符串的单元格是否与正则表达式匹配
'20\d{2}'
;如果是这样,则重新转换为 int
,否则保持单元格不变
testdf['col2'] = testdf.loc[phjStrMask,'col1'].apply(lambda c: int(c) if re.match('20\d{2}',c) else c)
col1 col1_types strmask col2
0 abc <class 'str'> True abc
1 2023 <class 'str'> True 2023
2 456 <class 'int'> False NaN
3 789 <class 'int'> False NaN
4 2021 <class 'str'> True 2021
5 4.5 <class 'float'> False NaN
6 123 <class 'str'> True 123
7 NaN <class 'float'> False NaN
第 3 步 - 最初包含字符串以外内容的单元格当前在
NaN
中表示为 col2
。创建一个掩码,指示 col2
中的哪些单元格包含 NaN
,然后替换为原始内容(在 col1
中)
nanmask = testdf['col2'].isnull()
testdf.loc[nanmask,'col2'] = testdf['col1']
col1 col1_types strmask col2
0 abc <class 'str'> True abc
1 2023 <class 'str'> True 2023
2 456 <class 'int'> False 456
3 789 <class 'int'> False 789
4 2021 <class 'str'> True 2021
5 4.5 <class 'float'> False 4.5
6 123 <class 'str'> True 123
7 NaN <class 'float'> False NaN
第 4 步 - 确认新列中的数据类型符合预期
testdf['col2_types'] = testdf['col2'].apply(type)
col1 col1_types strmask col2 col2_types
0 abc <class 'str'> True abc <class 'str'>
1 2023 <class 'str'> True 2023 <class 'int'>
2 456 <class 'int'> False 456 <class 'int'>
3 789 <class 'int'> False 789 <class 'int'>
4 2021 <class 'str'> True 2021 <class 'int'>
5 4.5 <class 'float'> False 4.5 <class 'float'>
6 123 <class 'str'> True 123 <class 'str'>
7 NaN <class 'float'> False NaN <class 'float'>
上面的过程看似可行,但却冗长且笨拙。有没有办法直接将
int
中与正则表达式匹配的字符串值重新转换为 col1
,而无需经过一系列中间步骤?
str.fullmatch
构建布尔掩码并更改 dtype:
m = testdf['col1'].str.fullmatch(r'\d{4}').eq(True)
testdf.loc[m, 'col1'] = testdf.loc[m, 'col1'].astype(int)