为 pandas.DataFrame.to_sql 添加进度条

18

我想将大型CSV文件的数据迁移到SQLite3数据库。

我的Python 3.5代码使用pandas:

con = sqlite3.connect(DB_FILENAME)
df = pd.read_csv(MLS_FULLPATH)
df.to_sql(con=con, name="MLS", if_exists="replace", index=False)

有没有办法打印出 to_sql 方法执行的当前状态(进度条)?

我查了一下关于 tqdm 的文章,但没找到如何做到这一点。

3个回答

38

不幸的是,DataFrame.to_sql没有提供逐块回调函数,这是tqdm更新状态所需要的。然而,你可以逐块处理数据框:

import sqlite3
import pandas as pd
from tqdm import tqdm

DB_FILENAME='/tmp/test.sqlite'

def chunker(seq, size):
    # from https://dev59.com/VHRC5IYBdhLWcg3wCMc6#434328
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

def insert_with_progress(df, dbfile):
    con = sqlite3.connect(dbfile)
    chunksize = int(len(df) / 10) # 10%
    with tqdm(total=len(df)) as pbar:
        for i, cdf in enumerate(chunker(df, chunksize)):
            replace = "replace" if i == 0 else "append"
            cdf.to_sql(con=con, name="MLS", if_exists=replace, index=False)
            pbar.update(chunksize)
            
df = pd.DataFrame({'a': range(0,100000)})
insert_with_progress(df, DB_FILENAME)

请注意,我在此处内联生成DataFrame,以便具有完整的可工作示例而不需要依赖项。

结果非常惊人:

输入图像描述


2
我的CSV文件在磁盘上占用了1.7GB的空间,所以df=pd.read_csv(csv_filename, ...)的速度非常慢。但是我在这里找到了解决方案:https://dev59.com/aGgu5IYBdhLWcg3wDS0G#28371706,所以你的答案和@sebastian-raschka的答案帮助我完成了这个任务。 - Andrei
1
使用range()代替xrange(),这在Python 3中也可以工作。非常好,我必须说! - Reinier

7

我想分享miraculixx发布的解决方案的一个变体,但我不得不为SQLAlchemy做出修改:

#these need to be customized - myDataFrame, myDBEngine, myDBTable

df=myDataFrame

def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

def insert_with_progress(df):
    con = myDBEngine.connect()
    chunksize = int(len(df) / 10)
    with tqdm(total=len(df)) as pbar:
        for i, cdf in enumerate(chunker(df, chunksize)):
            replace = "replace" if i == 0 else "append"
            cdf.to_sql(name="myDBTable", con=conn, if_exists=replace, index=False) 
            pbar.update(chunksize)
            tqdm._instances.clear()

insert_with_progress(df)

5
你定义了变量 replace 但是没有使用它。你是想使用 if_exists=replace 吗? - Maximilian Peters

0

用户miraculixx提供了一个很好的示例,非常感谢。但是如果您想将其用于所有大小的文件,您应该添加类似以下内容的代码:

chunksize = int(len(df) / 10)
if chunksize == 0:
    df.to_sql(con=con, name="MLS", if_exists="replace", index=False)
else:
    with tqdm(total=len(df)) as pbar:
    ...

你能完成上面发布的例子吗?当我将整数设置为“chunksize”变量时,只有那么多数量被记录到我的数据库中。例如,chunksize = int(len(df) / 10),那么只有总记录数的1/10被记录到我的数据库中。 - Pysnek313

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接