在 pandas 中有四个主要选项来转换类型:
to_numeric()
- 提供功能以安全地将非数值类型(例如字符串)转换为合适的数值类型。(另见to_datetime()
和to_timedelta()
。)astype()
- (几乎)任何类型转换为(几乎)任何其他类型(即使这样做不一定合理)。还允许你将类型转换为分类类型(非常有用)。infer_objects()
- 一个实用方法,如果可能的话,将包含 Python 对象的列转换为 pandas 类型。convert_dtypes()
- DataFrame 列转换为支持pd.NA
(pandas 的对象,用于表示缺失值)的“最佳可能”数据类型。
1. to_numeric()
将非数值对象(如字符串)转换为整数或浮点数的最好方法是使用 pandas
中的 to_numeric()
函数。这个函数会尝试将非数值对象(如字符串)转换为适当的整数或浮点数。
基本用法
to_numeric()
的输入可以是一个 Series
或者一个 DataFrame
的单列。
s = pd.Series(["8", "6", "7.5", "3", "0.9"]) # 合字符串和数值
s = pd.to_numeric(s) # 转换为浮点值
可以看到,返回了一个新的 Series
。你可以将其赋值给一个变量或列名以便继续使用:
my_series = pd.to_numeric(my_series)
你也可以使用 apply()
方法来转换 DataFrame
的多列:
df = df.apply(pd.to_numeric) # 转换 `DataFrame` 的所有列
或者只转换特定的列:
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)
只要你的值都可以被转换,这可能就是你需要的所有操作。
错误处理
但如果有些值无法转换为数值类型怎么办?
to_numeric()
接受一个 errors
关键字参数,允许你强制非数值值为 NaN
,或者简单地忽略包含这些值的列。
以下是一个使用字符串 Series
的示例,该 Series
有对象数据类型:
s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
默认行为是在无法转换值时引发异常。在这种情况下,它无法处理字符串 ‘pandas’:
pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string
与其失败,我们可能希望 ‘pandas’ 被视为缺失或无效的数值,并将其转换为 NaN
,如下所示:
pd.to_numeric(s, errors='coerce')
第三个选项是,如果遇到无效值,则忽略操作:
pd.to_numeric(s, errors='ignore')
原始 Series
将保持不变。
最后一个选项特别适用于转换整个 DataFrame
,但不知道哪些列可以可靠地转换为数值类型。在这种情况下,只需写:
df.apply(pd.to_numeric, errors='ignore')
该函数将应用于 DataFrame
的每一列。可以转换为数值类型的列将被转换,而不能转换的列(例如,它们包含非数字字符串或日期)将保持不变。
下采样 (Downcasting)
默认情况下,使用 to_numeric()
换时,你会得到 int64
或 float64
数据类型(或者平台原生的任何整数宽度)。
这通常是你要的结果,但如果你想要节省内存并使用更紧凑的数据类型,比如 float32
或 int8
么办?
to_numeric()
给你选择下采样的选项,可以转换为 'integer'
、'signed'
、'unsigned'
或 'float'
。以下是一个简单的整数类型 Series
示例:
s = pd.Series([1, 2, -7])
下采样到 'integer'
使用可以容纳值的最小可能整数:
pd.to_numeric(s, downcast='integer')
下采样到 'float'
同样选择比正常浮点类型更小的类型:
pd.to_numeric(s, downcast='float')
结果将是 float32
。
2. astype()
astype()
方法允许你明确指定 DataFrame 或 Series 的数据类型。它非常灵活,可以尝试从一种类型转换为另一种类型。
基本用法
只需选择一个类型:你可以使用 NumPy 数据类型(例如 np.int16
)、一些 Python 类型(例如 bool
),或者 pandas 特定的类型(如分类数据类型)。
在你想转换的对象上调用该方法,astype()
将尝试并为你进行转换:
# 将所有 DataFrame 列转换为 int64 类型
df = df.astype(int)
# 列 "a" 转换为 int64 类型,将列 "b" 转换为复数类型
df = df.astype({"a": int, "b": complex})
# 将 Series 转换为 float16 类型
s = s.astype(np.float16)
# 将 Series 转换为 Python 字符串
s = s.astype(str)
# 将 Series 转换为分类类型 - 请参阅文档以获取更多详细信息
s = s.astype('category')
请注意我说的是“尝试”——如果 astype()
不知道如何转换 Series 或 DataFrame 中的值,它将引发错误。例如,如果你有一个 NaN
或 inf
,尝试将其转换为整数时会得到错误。
从 pandas 0.20.0 开始,可以通过传递 errors='ignore'
来抑制此错误。你的原始对象将保持不变。
注意事项
astype()
是强大的,但它有时会“错误地”转换值。例如:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
这些是小整数,那么将其转换为无符号 8 位类型以节省内存如何?
>>> s.astype(np.uint8)
0 1
1 2
2 249
dtype: uint8
转换成功了,但 -7 绕了一圈变成了 249(即 (2^8 - 7))!
尝试使用 pd.to_numeric(s, downcast='unsigned')
行下采样可能会避免这个错误。
3. infer_objects()
pandas 版本 0.21.0 引入了 infer_objects()
方法,用于将 DataFrame 中具有对象数据类型的列转换为更具体的数据类型(软转换)。
例如,这里有一个 DataFrame,包含两列对象类型。一列持有实际的整数,另一列持有表示整数的字符串:
>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3', '2', '1']}, dtype='object')
>>> df.dtypes
a object
b object
dtype: object
使用 infer_objects()
,你可以将列 ‘a’ 的类型更改为 int64:
>>> df = df.infer_objects()
>>> df.dtypes
a int64
b object
dtype: object
列 ‘b’ 保持不变,因为其值是字符串,而不是整数。如果你想强制将两列都转换为整数类型,可以使用 df.astype(int)
。
4. convert_dtypes()
版本 1.0 及以上包括一个方法 convert_dtypes()
,用于将 Series 和 DataFrame 列转换为支持 pd.NA
缺失值的最佳可能数据类型。
这里的“最佳可能”是指最适合存储这些值的数据类型。例如,如果所有值都是整数(或缺失值),则会转换为 pandas 整数类型;如果 Python 整数对象的列被转换为 Int64
,NumPy int32
值的列将变为 pandas 数据类型 Int32
。
对于我们的对象 DataFrame df
,我们得到以下结果:
>>> df.convert_dtypes().dtypes
a Int64
b string
dtype: object
由于列 ‘a’ 包含整数值,因此被转换为 Int64
类型(能够存储缺失值,与 int64
不同)。
列 ‘b’ 包含字符串对象,因此被转换为 pandas string
类型。
默认情况下,此方法会从每列的对象值推断类型。我们可以通过传递 infer_objects=False
来更改这一点:
>>> df.convert_dtypes(infer_objects=False).dtypes
a object
b string
dtype: object
现在列 ‘a’ 仍然是一个对象列:pandas 知道它可以被描述为一个整数列(内部运行了 infer_dtype
),但没有推断出它应该具有的确切数据类型,因此没有转换它。列 ‘b’ 再次被转换为 string
类型,因为它被识别为持有字符串值。