根据经验,我提供的解决方法肯定不是最优的,因此,权当我在此抛砖引玉把。
我大致猜到楼主的目的,因为楼主相当于ID的那列变量是有规则重复,但不规则缺失的,因此,无法真正发挥ID的作用。我想,如果额外加上一列group.id,标明这行数据属于哪个组,应该可以解决该问题了。比如,最终的效果如下:
a group.id
max 1
unit 1
rate 1
unit 2
rate 2
max 3
unit 3
unit 4
max 5
unit 5
rate 5
这样子,这两列数据一并作为ID使用,使ID有了唯一性,作用可以发挥出来了。
另外,从上面的数据可以轻易得到,第1、5组数据是全的,第2组数据缺了"max"行,第3组数据缺了"rate"行,第4组数据既缺"max"行,也缺"rate"行,(这样每种缺失情况都包含了,等下的测试基本是该向量重复多次。不过,每组"unit"必不可少,不然问题会复杂很多),这样要补全数据也非常容易。
对于补全group.id,我有两个思路,第一个思路要借用“jiangbeilu”同学给出的:rep(c("max","unit","rate"),5)这个向量,这个向量是理想状态下的ID列,可惜和现有的缺失的dataframe无法一一对应,就好比我们去“配钥匙”,先买一把未挫过的“无齿钥匙”,然后用工具按照现有的钥匙的特征加工过,这把钥匙就能用了。
按第一个思路,首先,我们要根据数据框中的“unit”的数据推断出有多少组,假设为“n”,然后构建一把这样的“原始钥匙”:
然后,我再用“循环”和“递归”设计一个“配钥匙”的函数,基本思想是逐行检验现有数据和key数据,发现现有数据中没有但key数据框中有的,就丢掉key数据中的该行。函数返回新的key数据框:
测试一下,可用。万事大吉了吗?等等,楼主说过,他的原始数据比较大,因此,我要测试一下这种方法的效率。我用千万行级别的原始数据测试,内存不够;百万级别的,内存不够;十万级别的,告知递归得太深了;万级别的,还是递归得太深了;千行级别的,通过,但在我的老电脑上耗时大概10秒以上。
这样显然不行,这个思路不实用。当然,如果不用递归,而用更加复杂一些的循环方法,应该可以使数据处理的上限提高1到2个数量级,但仍无法改变其内存和CPU利用低效的事实。
第二个思路是“向量化”的处理方法。比如,我用下面的自编函数,arguments是现有数据框,和c("max", "unit", "rate")向量,函数返回的是加了group.id列后的现有数据框:
我测试过,可以运行1千万行以上的数据(当然,我的模拟数据中其他列数据不复杂),耗时5秒多,比第一个思路处理1千多行的数据还要快得多。
重要提醒:两个思路都假定头尾数据是完整的,因为头尾数据的判断和补全很容易,因此,未对头尾组缺失的情况进行容错设计。
通过这个案例,我自己也深刻地体会了R语言的长处和短处,如果用直观的方法,利用循环和递归,编程效率高,但执行效率太差;如果用向量化的方法去编程,思考的过程耗时比较长,但执行起来简直云泥之别,这可能就是R语言独特的魅力所在吧!