【发布时间】: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.loc 和 df = 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
而且我仍然需要类型转换,它们已经相当快了,但它们看起来并不优雅。
【问题讨论】:
-
你将把你的数据库存储在你可以使用
Sqlite的地方??? :stackoverflow.com/questions/36028759/…
标签: python mysql python-3.x pandas dataframe