我正在处理一个组织很差的数据库。有些customerid比int64大。示例:88168142359034442077.0
为了能够使用这个ID,我需要将其转换为字符串并删除小数点。我尝试使用以下代码:
testdf = pd.DataFrame({'CUSTID': ['99418675896216.02342351', '88168142359034442077.0213', '53056496953']})
testdf['CUSTID'] = testdf['CUSTID'].astype('float64').astype('int64').astype(str)
testdf.display()
当我使用上述方法时,我得到一个溢出,然后比int64大的数字变成负数,如:-9223372036854775808 for 88168142359034442077.0213
我一直在寻找其他的方法,能够使从字符串到float,然后float到int,最后int再到字符串的变化。
我尝试的一种方法是不使用astype('int64'),但它使输出变成科学格式,如:8.816814235903445e+19 for 88168142359034442077.0213,除了使用regex删除小数和'e+19'之外,我真的不知道我还能做什么。
欢迎提供任何信息。谢谢!
张贴作为答案,因为这变得太大了,我相信有进一步的价值
如果这些值是真实的和预期的id,而不是导入一些文本或二进制格式的错误工件,我会非常惊讶
具体来说,编写程序和数据库本身几乎肯定不会使用一些高内存十进制表示来表示客户标识符,而是使用"正常"表示。像int64这样的类型,如果它们完全以这种方式表示的话!
此外,浮点值使程序暴露于IEEE 754浮点混叠问题(请参阅浮点数学是否损坏?),这将巧妙地挫败各种查找和比较,并且通常无法愉快地或一致地表示这些值,因此不太可能有人合理地使用它们
一个人为的例子
>>> data1 = "111001111001110100110001111000110110110111110101111000111001110110110010110001110110101110110000110010110011110100110010110011110101110001"
>>> data2 = "111000111000110001110110111000110001110100110010110011110101111001110000110011110100110100110100110010110000110111110111101110110000110010110001110011"
>>> for data in (data1, data2):
... print("".join(chr(eval("0b" + data[block:block+6])) for block in range(0, len(data), 6)))
...
99418675896216.02342351
88168142359034442077.0213
这是一个很长的机会,但也许一个公平的怀疑,这可能发生在
- 用户正在输入一个新条目,但没有客户ID(还没有?)
- UI被编码为只接受数字字符串
- 没有其他检查,数据库将值存储为字符串
- 在发现这个问题时,用户通常会将基本无意义的字符混在一起,但检查传递字符到字段中以继续他们的工作
您可以尝试对它们进行另一次比较,例如,如果
- 它们都来自一个特定的用户
- 它们都来自一个特定的日期
- 字符串表示形式随着时间的推移而变长或变短(因为用户变得更懒或更不确定他们已经使用了一个值)
testdf['CUSTID']
是包含Python字符串对象的pandas.Series
对象。对于包含大整数的pandas.Series
对象,最直接使用的类型是int
Python对象(与更有效的本机Numpy类型相反)。您可以将其转换为Decimal
类型,以获得非整数部分。可以使用map
:
testdf['CUSTID'] = list(map(int, map(decimal.Decimal, testdf['CUSTID'].to_list())))
这不是很有效,但是Unicode字符串对象和大的可变大小的整数对象实际上都是低效的。由于Numpy本身不支持大整数,这当然是最好的选择(尽管可以找到比使用十进制包更快的方法来处理非整数部分)。
下面是一种基于字符串的解析方法,它当然比较慢,但支持非常大的整数,而不需要使用固定大小的大十进制精度:
testdf['CUSTID'] = [int(s.split('.')[0]) for s in testdf['CUSTID'].to_list()]
我建议将它们保留为字符串,并修剪.
:
import pandas as pd
testdf = pd.DataFrame({'CUSTID': ['99418675896216.02342351', '88168142359034442077.0213', '53056496953']})
testdf['CUSTID'] = testdf['CUSTID'].apply(lambda s: s[:s.find(".")])
testdf.display()
请注意,您可以用不同的东西替换:lambda s: s[:s.find(".")]
,但我不希望任何变化(例如lambda s: s.split(".", 1)[0]
或lambda s: re.match(r"^(d+)(?:.(d+))?$", s).groups()[0]
)比这更远。只需测试它们的一些示例输入,看看哪一个最适合您。
或者,您可能希望使用str
方法与extract()
熊猫系列,即:
testdf['CUSTID'] = testdf['CUSTID'].str.extract(r"^(d+)(?:.(d+))?$")
但我不确定这是否会比前面提到的解决方案更快。
也许你可以用rstrip()
实现更快的东西,但你的代码不会像上面那样简单,因为你需要在没有.
的情况下处理值,与其他的不同(no-op)。