
在64位Python环境中,我们通常期望Pandas在处理整数数据时默认使用int64类型,以充分利用系统架构的优势并避免潜在的溢出问题。然而,实际操作中可能会观察到一些不一致的行为。例如,当创建一个pd.Series并显式指定dtype=int时,其数据类型可能被推断为int32,即使在不指定dtype而让Pandas自动推断时,它可能正确地选择int64。
import pandas as pd
import platform
import sys
# 验证Python环境为64位
assert platform.architecture()[0] == "64bit"
assert sys.maxsize > 2**32
print(f"Python环境:{platform.architecture()[0]}")
# 显式指定 dtype=int
series_int_explicit = pd.Series([1, 2, 3], dtype=int)
print(f"pd.Series([1,2,3], dtype=int) 的类型: {series_int_explicit.dtype}")
# 不指定 dtype,让Pandas自动推断
series_int_inferred = pd.Series([1, 2, 3])
print(f"pd.Series([1,2,3]) 的类型: {series_int_inferred.dtype}")从上述代码的输出可以看出,即使在64位环境中,dtype=int的显式指定有时会导致Pandas选择int32,而自动推断则可能选择int64。这种差异本身可能不是一个功能性错误,因为int32足以存储大部分常见整数值,但在进行严格的DataFrame比较测试时,它会成为一个障碍。
Pandas提供了一个强大的测试工具pd.testing.assert_frame_equal,用于比较两个DataFrame是否相等。默认情况下,这个函数会执行非常严格的检查,包括数据类型(dtype)的精确匹配。这意味着,如果一个DataFrame的某一列是int32而另一个DataFrame的对应列是int64,即使它们包含相同的数值,assert_frame_equal也会因为类型不匹配而抛出AssertionError。
import pandas as pd
df_int32 = pd.DataFrame({'IntCol': [1, 2, 3], 'FloatCol': [0.5, 1.5, 2.5]})
df_int32['IntCol'] = df_int32['IntCol'].astype('int32')
df_int32['FloatCol'] = df_int32['FloatCol'].astype('float32')
df_int64 = pd.DataFrame({'IntCol': [1, 2, 3], 'FloatCol': [0.5, 1.5, 2.5]})
df_int64['IntCol'] = df_int64['IntCol'].astype('int64')
df_int64['FloatCol'] = df_int64['FloatCol'].astype('float64')
try:
pd.testing.assert_frame_equal(df_int32, df_int64)
print("DataFrame相等(包含类型)")
except AssertionError as err:
print(f"断言失败:\n{err}")输出清晰地表明,int32和int64被视为不同的类型,导致断言失败。虽然assert_frame_equal允许通过设置check_dtype=False来禁用类型检查,但这会使得测试失去对数据类型完整性的验证能力,从而可能掩盖潜在的问题。在许多场景下,我们希望验证数据内容和“等效”的数据类型,而非“精确”的数据类型。
为了解决上述问题,我们可以创建一个自定义的断言函数,它在比较DataFrame之前,将等效的数据类型(如int32和int64,或float32和float64)统一为同一种类型。这样既能保留类型检查的价值,又能允许在类型等效但具体位数不同的情况下通过测试。
以下是assert_frame_equiv函数的实现:
import pandas as pd
import numpy as np
def assert_frame_equiv(left: pd.DataFrame, right: pd.DataFrame) -> None:
"""
在比较前将等效数据类型统一,以实现更灵活的DataFrame等效性断言。
参数:
left (pd.DataFrame): 左侧DataFrame。
right (pd.DataFrame): 右侧DataFrame。
Raises:
AssertionError: 如果DataFrame不相等。
"""
# 1. 首先检查列名是否相同(顺序不重要)
pd.testing.assert_index_equal(left.columns, right.columns, check_order=False)
# 创建DataFrame的副本以避免修改原始数据
left_copy = left.copy()
right_copy = right.copy()
# 2. 遍历列,如果数据类型等效,则统一为右侧DataFrame的类型
for col_name in left_copy.columns:
lcol = left_copy[col_name]
rcol = right_copy[col_name]
# 检查是否为整数类型且等效(如int32 vs int64)
is_lcol_int = pd.api.types.is_integer_dtype(lcol)
is_rcol_int = pd.api.types.is_integer_dtype(rcol)
# 检查是否为浮点类型且等效(如float32 vs float64)
is_lcol_float = pd.api.types.is_float_dtype(lcol)
is_rcol_float = pd.api.types.is_float_dtype(rcol)
if (is_lcol_int and is_rcol_int) or (is_lcol_float and is_rcol_float):
# 如果是等效的整数或浮点类型,则将左侧列转换为右侧列的dtype
left_copy[col_name] = lcol.astype(rcol.dtype)
# 对于其他不兼容或非数值类型,保持不变,让assert_frame_equal处理
# 例如,如果一边是int,另一边是float,这里不会自动转换,
# pd.testing.assert_frame_equal会因dtype不匹配而失败,这是期望的行为。
# 3. 最后使用pd.testing.assert_frame_equal进行最终比较
# check_like=True 允许列和行的顺序不同,但我们已经在前面检查了列名。
# 这里的关键是经过类型统一后,dtype将匹配。
return pd.testing.assert_frame_equal(left_copy, right_copy, check_like=True)函数逻辑说明:
让我们使用之前定义的df_int32和df_int64来测试assert_frame_equiv函数。
# 重新定义DataFrame以确保干净状态
a = pd.DataFrame({'Int': [1, 2, 3], 'Float': [0.57, 0.179, 0.213]})
# 强制为32位类型
b = a.copy()
b['Int'] = b['Int'].astype('int32')
b['Float'] = b['Float'].astype('float32')
# 强制为64位类型
c = a.copy()
c['Int'] = c['Int'].astype('int64')
c['Float'] = c['Float'].astype('float64')
print("使用pd.testing.assert_frame_equal进行比较:")
try:
pd.testing.assert_frame_equal(b, c)
print('成功:DataFrame相等')
except AssertionError as err:
print(f'失败:\n{err}')
print("\n使用assert_frame_equiv进行比较:")
try:
assert_frame_equiv(b, c)
print('成功:DataFrame等效')
except AssertionError as err:
print(f'失败:\n{err}')通过assert_frame_equiv函数,尽管b和cDataFrame在内部使用了不同的整数和浮点位数,但由于它们的数据内容和等效类型一致,测试成功通过。这提供了一种在测试中处理灵活数据类型要求而又不完全牺牲类型检查的有效方法。
在64位Python环境中,Pandas Series在显式指定dtype=int时可能默认使用int32,这在与默认推断的int64或其他显式转换的类型进行严格比较时会引发问题。标准的pd.testing.assert_frame_equal函数由于其严格的类型检查机制,无法直接处理int32与int64之间的等效性。
本文提出的assert_frame_equiv自定义函数,通过在比较前智能地统一等效数值类型,为解决这一测试挑战提供了一个健壮且灵活的解决方案。它允许开发者在测试中关注数据内容的等效性,同时保留对数据类型大类的验证,避免了手动频繁转换类型或完全禁用类型检查的麻烦。
值得一提的是,Pandas社区也认识到这种需求,并已存在关于在pd.testing.assert_frame_equal中添加类似check_dtype='equiv'选项的功能请求(如GitHub issue #59182)。这表明未来Pandas版本可能会原生支持这种更灵活的类型比较方式,届时自定义函数的使用场景可能会有所变化。但在那之前,assert_frame_equiv提供了一个即时可用的强大工具。
以上就是Pandas整数类型默认行为与测试断言策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号