【问题标题】:Python 3 - How do I extract data from SQL database and process the data and append to pandas dataframe row by row?Python 3 - 如何从 SQL 数据库中提取数据并处理数据并逐行附加到 pandas 数据帧?
【发布时间】:2021-10-05 01:18:29
【问题描述】:

我有一个 MySQL 数据库,它的列是:

+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int unsigned | NO   | PRI | NULL    | auto_increment |
| artist       | text         | YES  |     | NULL    |                |
| title        | text         | YES  |     | NULL    |                |
| album        | text         | YES  |     | NULL    |                |
| duration     | text         | YES  |     | NULL    |                |
| artistlink   | text         | YES  |     | NULL    |                |
| songlink     | text         | YES  |     | NULL    |                |
| albumlink    | text         | YES  |     | NULL    |                |
| instrumental | tinyint(1)   | NO   |     | 0       |                |
| downloaded   | tinyint(1)   | NO   |     | 0       |                |
| filepath     | text         | YES  |     | NULL    |                |
| language     | json         | YES  |     | NULL    |                |
| genre        | json         | YES  |     | NULL    |                |
| style        | json         | YES  |     | NULL    |                |
| artistgender | text         | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

我需要从中提取数据并处理数据并将数据添加到 pandas DataFrame。

我知道如何从 SQL 数据库中提取数据,并且我已经实现了一种将数据传递给 DataFrame 的方法,但是它非常慢(大约 30 秒),而当我使用命名元组的平面列表时,操作是快得多(不到 3 秒)。

具体来说,文件路径默认为 NULL,除非文件已下载(目前没有下载任何歌曲),当 Python 获取文件路径时,该值将为“无”,我需要将该值变为 ''

由于 MySQL 没有 BOOLEAN 类型,我需要将收到的 ints 转换为 bool

语言、流派、风格字段是存储为 JSON 列表的标签,它们当前都是 NULL,当 Python 获取它们时它们是字符串,我需要使用 json.loads 将它们设为 lists,除非它们是 None ,如果它们是 None 我需要追加空列表。

这是我对问题的低效解决方案:

import json
import mysql.connector
from pandas import *

fields = {
    "artist": str(),
    "album": str(),
    "title": str(),
    "id": int(),
    "duration": str(),
    "instrumental": bool(),
    "downloaded": bool(),
    "filepath": str(),
    "language": list(),
    "genre": list(),
    "style": list(),
    "artistgender": str(),
    "artistlink": str(),
    "albumlink": str(),
    "songlink": str(),
}

conn = mysql.connector.connect(
    user="Estranger", password=PWD, host="127.0.0.1", port=3306, database="Music"
)
cursor = conn.cursor()

def proper(x):
    return x[0].upper() + x[1:]

def fetchdata():
    cursor.execute("select {} from songs".format(', '.join(list(fields))))
    data = cursor.fetchall()
    dataframes = list()
    for item in data:
        entry = list(map(proper, item[0:3]))
        entry += [item[3]]
        for j in range(4, 7):
            cell = item[j]
            if isinstance(cell, int):
                entry.append(bool(cell))
            elif isinstance(cell, str):
                entry.append(cell)
        if item[7] is not None:
            entry.append(item[7])
        else:
            entry.append('')
        for j in range(8, 11):
            entry.append(json.loads(item[j])) if item[j] is not None else entry.append([])
        entry.append(item[11])
        entry += item[12:15]
        df = DataFrame(fields, index=[])
        row = Series(entry, index = df.columns)
        df = df.append(row, ignore_index=True)
        dataframes.append(df)
    songs = concat(dataframes, axis=0, ignore_index=True)
    songs.sort_values(['artist', 'album', 'title'], inplace=True)
    return songs

目前数据库中有4464首歌曲,代码完成大约需要30秒。

我按艺术家和标题对我的 SQL 数据库进行排序,我需要按艺术家、专辑和标题对 QTreeWidget 的条目进行排序,而 MySQL 对数据的排序与 Python 不同,我更喜欢 Python 排序。

在我的测试中,df.locdf = df.append() 方法很慢,pd.concat 很快,但我真的不知道如何创建只有一行的数据框并将平面列表传递给数据框而不是字典,以及是否有比pd.concat 更快的方法,或者for 循环中的操作是否可以向量化。

如何改进我的代码?


我想出了如何使用列表创建一个 DataFrame 并指定列名,它的速度非常快,但我仍然不知道如何优雅地指定数据类型而不会引发代码错误......

def fetchdata():                                                                          
    cursor.execute("select {} from songs".format(', '.join(list(fields))))                
    data = cursor.fetchall()                                                              
    for i, item in enumerate(data):                                                       
        entry = list(map(proper, item[0:3]))                                              
        entry += [item[3]]                                                                
        for j in range(4, 7):                                                             
            cell = item[j]                                                                
            if isinstance(cell, int):                                                     
                entry.append(bool(cell))                                                  
            elif isinstance(cell, str):                                                   
                entry.append(cell)                                                        
        if item[7] is not None:                                                           
            entry.append(item[7])                                                         
        else:                                                                             
            entry.append('')                                                              
        for j in range(8, 11):                                                            
            entry.append(json.loads(item[j])) if item[j] is not None else entry.append([])
        entry.append(item[11])                                                            
        entry += item[12:15]                                                              
        data[i] = entry                                                                   
    songs = DataFrame(data, columns=list(fields), index=range(len(data)))               
    songs.sort_values(['artist', 'album', 'title'], inplace=True)                         
    return songs

而且我仍然需要类型转换,它们已经相当快了,但它们看起来并不优雅。

【问题讨论】:

标签: python mysql python-3.x pandas dataframe


【解决方案1】:

您可以为每一列制作一个转换函数列表:

funcs = [
    str.capitalize,
    str.capitalize,
    str.capitalize,
    int,
    str,
    bool,
    bool,
    lambda v: v if v is not None else '',
    lambda v: json.loads(v) if v is not None else [],
    lambda v: json.loads(v) if v is not None else [],
    lambda v: json.loads(v) if v is not None else [],
    str,
    str,
    str,
    str,
]

现在您可以应用转换每个字段的值的函数

for i, item in enumerate(data):
    row = [func(field) for field, func in zip(item, funcs)]
    data[i] = row

【讨论】:

  • 您的代码有两个拼写错误,根据您的代码,我认为fields 应该是dict
  • 抱歉 - 我最初想使用一个字典 - 也许希望将它与您的字段字典结合起来 - 但最终列表更适合。
  • 但它仍然没有演示如何正确设置每个字段的数据类型,例如我指定的field 字典。
  • 据我所知,DataFrame docs 无法为列设置不同的数据类型:您可以强制使用单一类型,也可以让 Pandas 推断。您可以使用infer_objects 方法改进初始结果。或者可能有其他方式 - 熊猫不是我的专长
猜你喜欢
  • 2018-08-16
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2016-10-13
  • 2021-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多