【问题标题】:Normalize CSV data before inserting into SQLite database with Pandas or plain SQL在使用 Pandas 或普通 SQL 插入 SQLite 数据库之前规范化 CSV 数据
【发布时间】:2018-05-23 05:33:56
【问题描述】:

这是我想要完成的图表:

实际的 CSV 文件可能有数万或数十万行,相当于 Name/Phone 组合,这会浪费大量内存和磁盘空间。我知道如何使用 SQL 将这些数据实际存储在数据库中后对其进行规范化,但我希望避免首先复制所有这些重复项,而是插入外键关系。这些列中的重复值占用的磁盘空间相当可观。

目前我正在使用 Python 和 Pandas 来自动化创建 SQLite 数据库并将数据插入其中的过程。我也为此尝试过 PostgreSQL,但由于某种原因发现它比 pandas+sqlite 慢。

如何在插入数据库之前将这些数据拆分为多个表?


编辑由于文件的大小,我将它们分块处理,如下所示:

import glob
import pandas as pd
import sqlite3


def read_nlines(file: str, n: int = 10) -> pd.DataFrame:
    return pd.read_csv(file, nrows=n, comment='#', skip_blank_lines=True,
                       sep='\t', encoding='utf-8')


conn = sqlite3.connect("data.db")
cur = conn.cursor()

files = glob.glob("data/*.txt")

for file in files:
    data = read_nlines(file)
###--->
###---> This is where I want to transform the data, before writing to sql <---###
###--->
    data.to_sql('table',con=conn,if_exists='replace')

conn.close()

【问题讨论】:

  • 那么真正的问题是什么?

标签: python postgresql sqlite pandas csv


【解决方案1】:

您可以使用pandas

df=pd.DataFrame({'Name':['Joe','Bob','Bob','Bob','Steve','Steve','Steve'],'Phone':['111','222','222','222','333','333','333'],'S1':[1,2,3,4,5,6,7],'S2':[1,2,3,4,5,6,7],'S3':[1,2,3,4,5,6,7]})
df['ID']=pd.factorize(df['Name'])[0]+1
sql1=df[['ID','S1','S2','S3']]
sql2=df[['ID','Name','Phone']].drop_duplicates()
sql1
Out[729]: 
   ID  S1  S2  S3
0   1   1   1   1
1   2   2   2   2
2   2   3   3   3
3   2   4   4   4
4   3   5   5   5
5   3   6   6   6
6   3   7   7   7
sql2
Out[730]: 
   ID   Name Phone
0   1    Joe   111
1   2    Bob   222
4   3  Steve   333

编辑:

ncount=1
for file in files:
    data = read_nlines(file)


    data['ID'] = pd.factorize(data['Name'])[0] + ncount
    ncount+=data['Name'].nunique()
    sql1 = df[['ID', 'S1', 'S2', 'S3']]
    sql2 = df[['ID', 'Name', 'Phone']].drop_duplicates()

【讨论】:

  • 有什么方法可以跨调用进行这项工作吗?这些文件需要分块处理,我想知道如何保留factorize 的结果,以便在不同的数据帧上运行它不会重置factorize 提供的索引?
  • @mooglinux 你如何在这里进行块? ,如果在for循环中,需要多加一个参数
  • 参数有什么用?类别?
  • 添加到原始问题。
  • @mooglinux 检查更新:-) 你会看到 ncount(新参数)
【解决方案2】:

随意使用您喜欢的任何数据结构,但这对我来说是有意义的。

将 csv 文件中的每一行加载到包含两个字典的列表中。一本字典用于您的个人相关领域,一本用于其他领域。您可以为此使用 csv 库 - 但我想如果您已经做到了这一点,那么您已经为此制定了一个流程。

然后,创建个人字典列表,如果字典不在列表中,则仅添加字典。

然后,您可以遍历您的原始列表列表,并将 personKey 键添加到每个子列表中的第二个字典。将此列表的键设置为您的人员字典列表中人员字典的索引。

dictList = [
    [{'Name': 'Bob', 'Phone': '098-765-4321'}, {'S1': 0.36, 'S2': 0.91, 'S3': 0.24}],
    [{'Name': 'Bob', 'Phone': '098-765-4321'}, {'S1': 0.44, 'S2': 0.36, 'S3': 0.78}],
    [{'Name': 'Steve', 'Phone': '768-098-1234'}, {'S1': 0.55, 'S2': 0.15, 'S3': 0.74}]
]

personRecords = []
numberRecords = []
for item in dictList:
    numberRecord = item[1]
    if item[0] not in personRecords:
        personRecords.append(item[0])
    numberRecord['personKey'] = personRecords.index(item[0])
    numberRecords.append(numberRecord)

for person in personRecords:
    print(person)

for record in numberRecords:
    print(record)

然后您可以获取每个字典列表,将它们解析为您的数据库库所需的任何数据结构,然后上传。

【讨论】:

    猜你喜欢
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 2011-11-17
    • 1970-01-01
    • 2013-01-18
    • 2020-10-21
    • 1970-01-01
    相关资源
    最近更新 更多