在外存中实现分组的代码示例 文件计算外存分组代码集算器
在数据分析中,我们经常需要将数据分组,然后计算出各组的汇总值,或者在各组中分别计算。集算器中,可以用groups函数计算数据的分组汇总结果,更可以用group函数将表中记录分成多组,以便后续计算。但是,如果需要排序的数据量巨大,情况就不同了,这时是不能一次将它们读入内存的,这样普通的分组汇总,或者分组的方法就无法执行了,此时就可能需要使用外存分组。
下面,先来准备一个大数据表,简单模拟1,000,000条手机用户的通话时长记录,存储在二进制文件PhoneBill中:
其中,电话号码使用8位整数,前4位固定为1234,后4位随机生成。通话开始时间在2014年8月中随机产生。通话时长为整数,也随机生成,产生时使通话时长有90%的可能性为1分钟,最长20分钟。数据文件完成后,在B11中读出前1000条如下:
现在,需要根据PhoneBill中的数据计算:
① 8月中每天所有用户的总通话时长以及每次通话的平均时长。
② 每位用户在8月的通话时长。
③ 分别将每天的通话记录存储到文件中。
④ 8月的每一天中,通话总时长最多的5位用户号码。
先来看问题①,这首先需要将所有的订单数据按日期来分组汇总。因为8月一共只有31天,所以结果集并不是大数据,可以直接在内存中汇总计算:
A2用A1中的二进制文本数据生成游标。A3计算游标中数据的分组汇总结果如下:
因为结果本身的数据量并不大,计算分组汇总时,其实只需要将游标中的数据遍历一次,并不需要外存分组,直接用groups函数就能计算。这里的结果集并不庞大,可以直接获得结果,并不需要使用游标。计算每天中,每次通话的平均时间,需要先统计出每天的通话总时间及总次数,再进一步计算出平均通话时间,A4中获得的结果如下:
问题②中的情况就不同了,由于用户量要比8月的天数多得多,这时就需要考虑分组汇总的结果是不是能一次返回到内存中。在这里,最多只有10,000个用户,并不算太多,只是以此来说明大数据结果集的计算情况。计算时,假设内存中只能容纳1000条记录:
在计算大数据分组汇总结果时,需要用groupx函数,利用外存,根据设定的缓冲区行数来将游标内的数据逐步读出分组。A3中,计算游标中数据,按Phone分组;汇总计算总通话时长的结果,同时设定缓冲区行数为1000。大数据分组汇总的结果是游标,读取数据的方法和普通的游标完全相同。A3中的结果如下:
在A4中读出了前1000名用户的总通话时长结果:
另外,游标中的数据如果没有全部读完,需要调用cs.close()函数,以及时清理外存临时文件。
问题③需要先将数据按照日期分组,再将每天的数据存储到文件中:
使用groupn函数的大数据分组和普通的分组不同,需要在分组表达式中直接指定组号,在A3中,用订单日期的“日”作为组号。A3的函数中计算的结果与前面的分组汇总不同,返回的是游标序列,每个分组对应1个游标:
在第4、5行的代码中,将每天的游标数据分别存到文件中。在A6中,选择了其中4日的数据,在A7中读出了前1000条记录如下:
问题④仍然将数据按照日期分组,再在每个分组中执行分组汇总,最后根据汇总结果选出所需的用户号码:
A3中,仍然在分组表达式中直接指定“日”作为组号,分组时返回的仍然是游标的序列:
第4行到第6行的代码循环每个分组的游标数据,分别汇总每个用户每天的总通话时间,并根据汇总结果再次汇总出通话时间排在前5名的用户信息。需要注意的是,用汇总函数topx计算降序排序时,在排序表达式前加负号即可,如B5中的-TotalDuration。在结果中,选出通话时间最长的5个号码数据,并存入B3的序表中。循环完毕后,可以从B3中读出最终的结果如下:
除了将总的话费数据分组,也可以继续使用问题③中生成的文件数据来处理这个问题。
上一篇: Mysql将数据分组后取出时间最近的数据
下一篇: 简化SQL计算之字母分段