python和q在数据分析中的简单比较

翻译一篇文章,作者为Ferenc Bodon,文章同时发表在kx blog上,附上文章链接:http s : / / kx.com /blog/a-comparison-of-python-and-q-for-data-analysis/

Ferenc Bodon是kdb+专家,以及在其他几种编程语言的经验丰富的软件开发人员,是一位具有数据挖掘和统计学术背景的软件架构师,多年来发表了许多技术文章。可以关注LinkedIn上的Ferenc,查看他关于编程语言比较的系列文章。Ferenc最初发表这篇文章的标题是:用于数据分析的Python……真的很简单吗(Python for data analysis… is it really simple)?!?

Python是一种流行的编程语言,易于学习,高效并且得到了广大社区的支持。它的主要数据分析库Pandas在数据科学家和数据工程师中越来越受欢迎。Pandas遵循Python的原则,因此它似乎易于学习、阅读,并允许快速开发……至少根据教科书上的例子是如此。

但是,如果我们离开教科书示例的安全便捷的世界,会发生什么?Pandas是否仍然是一个易于使用的用于查询表数据的数据分析工具?在本文中,我将通过执行基于多列的聚合来实现一个简单用例的示例。我的用例并不是人为编造的不切实际的案例。任何分析数据表的人都可能会遇到这个基础问题。为了进行比较,我将提供与某些Python表达式等效的SQL和q/kdb+表达式。您可以在GitHub上找到源代码。

基于表的基础数据分析包括:分组计算;计算多列之和;计算单个列的多个聚合(求和和平均值);计算加权平均值等。

让我们正式来看一个简单案例。我们有一个四列表格:

t = pd.DataFrame({‘bucket’:[‘a’, ‘a’, ‘b’, ‘b’, ‘b’], ‘weight’: [2, 3, 1, 4, 3], ‘qty’: [100, 500, 200, 800, 700], ‘risk’: [10, 20, 12, 60, 58]})

图片

我们希望计算每个bucket的如下内容:1、元素数目NR;2、qty和risk的总和和平均值,TOTAL_QTY/TOTAL_RISK和AVG_QTY/AVG_RISK;3、qty和risk的加权平均值W_AVG_QTY和W_AVG_RISK,权重由weight列给出。

上述的每个聚合都是很基础的。我们不会使用任何Pandas文档不推荐的方法,例如通过嵌套字典重命名聚合等。让我们依次解决上述问题。

1、 计算元素数目

获取每个bucket中的元素数量,需要大量输入:

t.groupby(‘bucket’).agg({‘bucket’: len}).rename(columns= {‘bucket’:‘NR’})

图片

这里使用了三次literal bucket,用了五个括号。

相比之下,SQL语句的写法为:

SELECT COUNT(*) AS NR, bucket FROM t GROUP BY bucket

q/kdb+的写法更加简洁优雅,不需要任何的括号或重复的文字:

select NR: count i by bucket from t

2、 多列的聚合

使用Pandas,获取单个列的总和和平均值以及获取多个列的总和非常简单:

t.groupby(‘bucket’)[‘qty’].agg([sum,np.mean]).rename(columns={‘sum’: ‘TOTAL_QTY’, ‘mean’: ‘AVG_QTY’})

图片

t.groupby(‘bucket’)[‘qty’,‘risk’].agg(sum).rename(columns={‘qty’: ‘TOTAL_QTY’, ‘risk’: ‘TOTAL_RISK’})

图片

如果尝试将两种方法结合使用,则代码会很糟糕,因为这会导致列名冲突。需要引入Multi-level columns和map函数:

res = t.groupby(‘bucket’).agg({‘qty’: [sum, np.mean], ‘risk’: [sum, np.mean]})

res.columns = res.columns.map(’_’.join)

res.rename(columns{‘qty_sum’:‘TOTAL_QTY’,‘qty_mean’:‘AVG_QTY’,
‘risk_sum’:‘TOTAL_RISK’,‘risk_mean’:‘AVG_RISK’})

图片

q / kdb +等效写法不需要引入任何新概念,新聚合仅用逗号分隔。您可以使用关键字sum和avg分别获取总和和平均值,例如:

select
TOTAL_QTY: sum qty,
AVG_QTY: avg qty,

by bucket from t

3、计算加权平均

Pandas所依赖的Numpy库支持加权平均值。不幸的是,它不能以与np.sum相同的方式使用。您需要将它包装在lambda表达式中,使用apply代替agg,并从系列中创建一个data frame。

t.groupby(‘bucket’).apply(lambda g: np.average(g.qty, weights=g.weight)).to_frame(‘W_AVG_QTY’)

图片

同样,q/kdb+解决方案不需要引入任何新概念,使用wavg函数即可:

W_AVG_QTY: weight wavg qty

4、合并实现

让我们在一段代码中实现上述功能:

res = t.groupby(‘bucket’).agg({‘bucket’: len, ‘qty’: [sum, np.mean], ‘risk’: [sum, np.mean]})
res.columns = res.columns.map(’_’.join)
res.rename(columns={‘bucket_len’:‘NR’,qty_sum’:‘TOTAL_QTY’,‘qty_mean’:‘AVG_QTY’, ‘risk_sum’:‘TOTAL_RISK’,‘risk_mean’:‘AVG_RISK’}).join(
t.groupby(‘bucket’).apply(lambda g: np.average(g.qty, weights=g.weight)).to_frame(‘W_AVG_QTY’)).join(
t.groupby(‘bucket’).apply(lambda g: np.average(g.risk, weights=g.weight)).to_frame(‘W_AVG_RISK’))

图片

要得到最终结果,您需要三个表达式和一个临时变量。如果花费多一点时间学习,你会发现这种复杂性部分归因于函数agg中不支持嵌套字典。另外,还有另一种使用较少的方法,只使用函数apply,不需要连接。

def my_agg(x):
data = {‘NR’: x.bucket.count(),
‘TOTAL_QTY’: x.qty.sum(),
‘AVG_QTY’: x.qty.mean(),
‘TOTAL_RISK’: x.risk.sum(),
‘AVG_RISK’: x.risk.mean(),
‘W_AVG_QTY’: np.average(x.qty, weights=x.weight),
‘W_AVG_RISK’: np.average(x.risk, weights=x.weight)
}
return pd.Series(data, index=[‘NR’, ‘TOTAL_QTY’, ‘AVG_QTY’, ‘TOTAL_RISK’, ‘AVG_RISK’, ‘W_AVG_QTY’, ‘W_AVG_RISK’])
t.groupby(‘bucket’).apply(my_agg)

图片

此解决方案需要创建一个临时函数,该函数可能永远不会在源代码中再次使用。此外,第二种方法在中等大小的表上速度较慢。

让我们来看看q/kdb+如何解决:

select NR: count i,
TOTAL_QTY: sum qty, AVG_QTY: avg qty,
TOTAL_RISK: sum risk, AVG_RISK: avg risk,
W_AVG_QTY: weight wavg qty,
W_AVG_RISK: weight wavg risk
by bucket from t

我想我们都同意,这是一个更直观,更简单,更可读的解决方案。不需要创建临时变量(或函数)。

5、性能表现如何?

根据我的实验,q解不仅更优雅,而且速度快了一个数量级。实验是在Windows和Linux上进行的,使用稳定的最新二进制文件和库。查询被执行了上百次,Github上提供了测试脚本。下表总结了以毫秒为单位的执行时间。将这两个Python解决方案与单个q查询进行比较。在q中,输入表的bucket列的类型可以是字符串或smybol(首选)。

图片

(注:Github网址为http s : / / gist. github.com /BodonFerenc /328628f67217955e30e56b02e670259f)

说明:本文经授权转载自微信公众号kdbcnbook。
kdbcn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值