【发布时间】:2018-09-30 12:22:06
【问题描述】:
背景
我正在与加拿大水调查局的HyDat Database 合作,其中包括 8000 多个水文测量站。我已经编写了查询每日流量数据的代码:
conn = create_connection('db/Hydat.sqlite3')
cur = conn.cursor()
cur.execute("SELECT * FROM DLY_FLOWS WHERE STATION_NUMBER=?", (station,))
将返回的数据放入数据框后:
rows = cur.fetchall()
column_headers = [description[0] for description in cur.description]
df = pd.DataFrame(rows, columns=column_headers)
数据大致如下格式:
STATION_NUM YEAR MONTH ... FLOW1 FLAG1 FLOW2 FLAG2 ...
02QC003 1965 02 ... 32.5 E 33.4 A ...
02QC003 1965 03 ... 44.6 E 45.4 A ...
02QC003 1965 04 ... 54.3 E 56.2 A ...
... ... ... ... ... ... ... ... ...
FLOWN 和 FLAGN 列中的 N 分别从 1 到 31 对应于一个月中的日期(需要在
我正在尝试提高查询数据并将数据重塑为以下每日时间序列格式的性能:
STATION_NUM YEAR MONTH DAY FLOW FLAG
02QC003 1965 02 1 32.5 E
02QC003 1965 02 2 33.4 A
02QC003 1965 02 3 33.7 A
... ... ... ... ... ...
我试图转换为行的每日值的数量高达 ~1000(大约相当于 100 年,其中一行代表一个月,每日值以列为单位)。处理几个查询不是问题,但我的目标是约 40M 查询。目前,我正在使用 Pandas melt 函数,首先用于每日流量,然后用于数据标志(为简洁起见,未显示):
id_var_headers = column_headers[:11]
all_val_vars = [e for e in column_headers if 'FLOW' in e]
flow_val_vars = [e for e in all_val_vars if '_' not in e]
df_flows = pd.melt(df,
id_vars=id_var_headers,
value_vars=flow_val_vars,
value_name='DAILY_FLOW',
var_name='DAY').sort_values(by=['YEAR', 'MONTH'])
df_flows['DAY'] = df_flows['DAY'].apply(
map_day_to_var_name)
def map_day_to_var_name(s):
if re.search('\d', s):
return s[re.search('\d', s).span()[0]:]
我发现整个序列中第二慢的操作在 10^-3 秒内运行,限制步骤是 melt 函数,它似乎慢了 10 倍。我希望在这一步上实现 10 倍或更好的改进。
我尝试以此为契机了解更多有关 SQLite 的信息,并花了一些时间试图弄清楚如何构建查询以查看我的 ETL 流程的“转换”部分是否可以合并为一个步骤并执行比熊猫好。我想出的东西在理论上可行(参见this SQLFiddle),但我正在努力在我的代码中实现它。 有关详细信息,请参阅 2018-05-02 更新。
This answer from user piRSquared 似乎与我所追求的非常接近,尽管我被困在groupby 功能步骤上。按照 piRSquared 的回答中概述的步骤,我预计年份和月份会扩大以取消每日价值,这让我相信我错误地应用了 groupby 函数。
非常感谢任何帮助,以及对我如何提出问题的任何反馈(这是我第一次发布问题)。
2018-04-20 更新wide_to_long
Scott Boston 的建议更整洁,尽管我需要添加一些步骤:
首先,我填充了当月的天数 df.rename(columns={'FLOW1': 'FLOW01', ...}, inplace=True)
因为我需要一个DAY 列,所以我也省略了.drop('VARIABLE', axis=1) 来创建行:
df = pd.wide_to_long(raw_df, ['FLOW', 'FLOW_SYMBOL'], idx_cols,
'DAY', sep='', suffix='.').reset_index()
在每天有 76K 条记录的记录上进行测试,我使用 melt 函数得到 ~0.1s,使用 wide_to_long 函数得到 ~0.6s。
还有其他方法可以改进这一步吗?
2018-05-02 更新
我回去检查了大致代表边界长度的查询的响应时间。对于约 10 行(短记录)和 > 1K 行之一(大约是数据库中最长的记录周期)的查询,我分别得到每个查询 0.04 到 0.1 秒的范围。这个结果向我表明,一个更好的 SQL 查询不会比一个简单的查询更好,然后是 pandas melt 函数。
因此,我认为我目前的流程已达到预期效果。
【问题讨论】:
标签: python pandas sqlite merge melt