在大型CSV数据库中使用to_datetime转换混合日期格式时发生转换错误

gopyfrb3  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(93)

我有一个很大的合约CSV数据库(2500万行,大约7 GB)。我需要过滤掉过期的合约,以减少进一步计算的大小。过期日期数组包含混合格式的日期(dd/mm/yyyy和dd/mm/yyyy hh/mm/ss)。
我试着用这个代码:

#reading database
reader = pd.read_csv(path_contracts, sep="|", header=0, low_memory=False, chunksize=1000000)   
output = "Base_Filtered.csv"
 
#filtering chunks
for contracts in reader:
        contracts[name_date_end] = pd.to_datetime(contracts[name_date_end], dayfirst=True, format='mixed')
        #conditional
        contracts = contracts[(contracts[name_date_end]>=date_report)]  
        #outputing filtered chunks     
        contracts.to_csv(output, sep="|")

字符串
但我得到了这个错误:

pandas._libs.tslibs.np_datetime.OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 31.08.8020, at position 3584


我检查了位置3584,它返回06.11.2021:

print(contracts.at[3584,name_date_end])
06.11.2021


因此,我尝试制作一个较小版本的数据库(5 k行),其中包含此元素,并且它工作得非常好。
在尝试过滤数据块之前,我尝试过一次过滤整个数据库。它产生了相同的错误,但在不同的位置。
无法找出问题的根源和解决方案。

pvabu6sv

pvabu6sv1#

在我看来,你只需要两种日期或日期时间格式,而你的数据不止这些,但你不知道它们可能是什么。
对于如此大的数据集,很难事先询问它并使一切完美,所以也许你需要迭代和交互地运行这个过程:

  • 任何日期时间与预定义格式不匹配的行都会被踢出到一个单独的文件中
  • 然后,您可以检查这个“bads”文件,并找出需要添加到主脚本中的新格式

我模拟了一个非常简单的输入CSV:

1,1/1/2001
2,2/2/2002 12/34/56
3,3.3.2003
4,6.1.2001
5,7.1.2001-5:38:19

字符串
以下脚本:

  • 定义预期的日期时间格式列表
  • 打开两个文件进行写入,并在它们周围创建csv.writers
  • 开始阅读输入csv:
  • 它尝试所有预定义的日期时间格式,直到成功并返回,或者
  • 如果不成功,则返回None
  • 如果前一步没有成功,该行将被写入bads进行分析,否则...
  • 它通过良好的日期时间进行过滤,记录/打印过滤出的行(仅用于调试,在这个非常小的示例数据集上);它还将日期时间转换为ISO格式
import csv
from datetime import datetime

fmts = [
    r"%d/%m/%Y",
    r"%d/%m/%Y %H/%M/%S",
]

def parse_dt(s: str) -> datetime | None:
    for fmt in fmts:
        try:
            dt = datetime.strptime(s, fmt)
            return dt
        except ValueError:
            continue

    return None

filtered = csv.writer(
    open("output_filtered.csv", "w", newline=""),
    delimiter=",",
)

bad = csv.writer(
    open("output_bad.csv", "w", newline=""),
    delimiter=",",
)

reader = csv.reader(
    open("input.csv", newline=""),
    delimiter=",",
)

for row in reader:
    dt = parse_dt(row[1])

    if dt is None:
        bad.writerow(row)
        continue

    if dt < datetime(2002, 1, 1):
        row[1] = dt.isoformat()  # standardize on ISO format
        filtered.writerow(row)
    else:
        print(f"dropped {dt}")


这将生成以下CSV:

output_bad.csv:
3,3.3.2003
4,6.1.2001
5,7.1.2001-5:38:19

output_filtered.csv:
1,2001-01-01T00:00:00


并将以下内容打印到stdout:

dropped 2002-02-02 12:34:56


然后,我会取3.3.20037.1.2001-5:38:19,用r"%d.%m.%Yr"%d.%m.%Y-%H:%M:%S"更新fmt:

fmts = [
    r"%d/%m/%Y",
    r"%d/%m/%Y %H/%M/%S",
    r"%d.%m.%Y",
    r"%d.%m.%Y-%H:%M:%S",
]


以及:

dropped 2002-02-02 12:34:56
dropped 2003-03-03 00:00:00

bads CSV为空,过滤后的CSV如下所示:

1,2001-01-01T00:00:00
4,2001-01-06T00:00:00
5,2001-01-07T05:38:19

相关问题