当我们用pandas处理百万级,千万级,乃至上亿行的数据时,缓慢的速度常常让我们痛苦不堪。
这时候,不妨试试duckdb.
duckdb在本地单机即可运行,性能非常高。
它可以像spark那样使用sql语句进行数据分析和数据转换。
当处理几千万行以上的数据时,它的效率通常是pandas的几十几百倍。
公众号算法美食屋后台回复关键词:源码,获取本文notebook源代码。
duckdb个库的用法非常简单,核心API只有以下几个。
import duckdb
#输入建表:可以从parquet,pandas dataframe建表
tb = duckdb.read_parquet('input_data.parquet')
tb = duckdb.from_df(df) #从pandas转换
#分析转换:执行sql,支持自定义函数UDF
tb2 = duckdb.sql('select * from tb where val>1000 and val<10000 order by val')
duckdb.create_function('myfunc',myfunc,[duckdb.typing.VARCHAR],duckdb.typing.VARCHAR)
#输出落表: 推荐导出成parquet格式,效率最高
tb2.to_parquet('output_data.parquet')
df2 = tb2.to_df() #也可以转换成pandas
一,输入建表
import numpy as np
import pandas as pd
import duckdb
#生成示例数据(5000万行)
dfdemo = pd.DataFrame(
{
'category': np.random.choice(list('ABCDEF'), 50000000),
'val': np.round(np.random.uniform(0, 1000000, 50000000), 3)
}
)
dfdemo.to_parquet('dfdemo.parquet', index=False)
对比读取数据速度,duckdb比pandas快几十倍
二,SQL分析
1, 基本查询
where查询对比 (duckdb比pandas快20倍)
groupby 操作对比 (duckdb比pandas快40倍)
join 操作对比 (duckdb比pandas快600倍)
2,自定义函数UDF
pyspark的一个优点是可以在sql中使用注册自定义函数(UDF),比较灵活。
那么duckdb支持在sql中使用注册自定义函数吗?of course!
⚫️ 注册方法:使用create_function方法来注册一个Python函数作为UDF。需要提供函数名称、Python函数、参数类型和返回类型。
⚫️ 类型注解:如果Python函数有类型注解,可以省略parameters和return_type参数,DuckDB会根据注解自动推断。
⚫️ 空值处理:默认情况下,当UDF接收到NULL值时,会立即返回NULL。如果需要特殊处理,可以设置null_handling="special"。
⚫️ 异常处理:默认情况下,如果Python函数抛出异常,DuckDB会重新抛出该异常。如果希望改为返回null,可以设置exception_handling="return_null"。
⚫️ 副作用:如果UDF的结果受随机性影响,需要将side_effects设置为True。
⚫️ 使用Arrow:如果函数需要接收Arrow数组,设置type='arrow'。这会通知系统提供Arrow数组给函数,并期望函数返回相同数量的数组。
⚫️ 使用Native:当设置type='native'时,函数将按单个元组接收数据,并返回单个值。这适用于与不操作Arrow的Python库交互,如faker。
import duckdb
from faker import Faker
def generate_random_name(i:int) -> str:
fake = Faker()
fake.random.seed(i)
#演示异常逻辑处理
#if i%10==0:
# raise Exception('error')
name = fake.name()
return name
#移除UDF
if 'random_name' in get_rand_funs()['function_name'].tolist():
duckdb.remove_function('random_name')
#注册UDF
duckdb.create_function("random_name", generate_random_name,
[duckdb.typing.BIGINT], duckdb.typing.VARCHAR,
exception_handling="return_null")
三,输出落表
tb_student.to_parquet('student.parquet')
公众号算法美食屋后台回复关键词:源码,获取本文notebook源代码。
万水千山总是情,点个在看行不行?😋😋