一个自动化数据筛选的问题的解决--基于R语言
这个问题和解决来自于我的工作中。这篇文章的重点是,它不是介绍 R 语言入门的文章,是面向问题的,所以我抱着你能够看懂文中出现的代码的希望来写,代码又少又简单。
问题的描述是这样的,给定一个数据框,对于数据框中的字符型变量 A, 对任意组合的条件,产生新的数据框。
简单来说这里说描述的问题就是你几乎每天都在使用的Excel表格中的变量筛选所解决的问题。为了逻辑连贯,我在这里应该说明解决这个问题的意义。出于简洁的目的,我认为读者知道这个意义,而不再列举。我还是很乐意简单地说,为了自动化。我还没用发现一个r包可以解决这个问题,subset 函数是不行的,在后面会讨论到,如果有读者发现有某个函数已经解决了这个问题,欢迎告诉我。任意基于何冯·诺依曼的计算机自动化的充分条件是一段程序。所以我们需要一段代码来实现自动。从面向对象编程的观点来说,就是为数据框类定义一个方法,它的参数是一个序列,返回值是一个数据框对象。从函数是一个黑盒子的观点来看,subset 函数不可用的原因是它的 subset 参数不是向量,而是标量。当不确定有多少个标量时,我们不知道如何写和 subset 有关的代码。
一个实例
给定数据框 df, df中一个变量A,其中A={a, b, c},给定一个序列A~={a,c},返回一个以A~为变量的新数据框 df~。
使用过R的小伙伴都知道,应该使用subset函数.
subset(df, subset = (df$A == a | df$A == c), select = c(A, B))
让人感觉良好的是,它解决了给出的那个实例。生活总是给我教训,在我每当感觉良好的时候。
但是如果给定的序列为{b, c},你不得不在代码中修改,如果对每一种情况改一次代码,那么注定与自动化说拜拜了。从定义来说使用 subset 并不能写出一个算法,算法的定义要求算法应该能解决一类问题(我认为求一组任意个数整数的最大值是一个整数最大值算法,而只能求几个,不管多少个都不能看作算法)。subset函数是行不通的,因为使用subset对问题的泛化无能为力。
如果有一个解(算法),对于给定数据框 Q, 不乏一般性地,假设Q只有A, B两个观测(变量),其中观测A有x1,x2,...,xn个水平,对于k个来自观测A的水平的所组成的向量C={xi1,xi2,...xik},k<=n,能产生新由C和筛选出来的B组成新的数据框P。
连贯的逻辑下,写到这里我应该证明我的代码对上面这个模型总能得到想要的输出。我觉得这种工作应该留给数学家或者计算机专家,但我还是很乐意去尝试。首先给出伪代码:
zero <- FALSE;
x <- FALSE;
if k = 1
x <- Q$A == C[1] | zero;
else
i from 2 to k
x <- Q$A == C[i] | x;
C=Q[which(x), ];
// 可以化简为
x <- FALSE;
i from 1 to k
x <- Q$A == C[i] | x;
C = Q[which(x), ]
证明:
对于简化后的代码,第一行到第三行都属于一个循环体,第4行代码必定正确,因为是使用别人的函数。所以只证明循环部分是正确的。
假设:Q$A为整数,且已经排列。C已排列。计算环境中所有的量都是向量。
初始化:x<-FALSE,算法刷选了0个,把选择0个作为一个退化,显然满足我们的预期(它没有选择不该选择的行,所以是正确地)。
保持:如果进行到第i次之前,那么循环前x=(T,T,...F,F,...)是我们希望的,有i-1个TRUE,循环后x<-x|(F,F,...,T,F,F,...)=(T,T,...,F,F,...),有i个TRUE也是我们希望的,所以这里有个循环不变式。
总止:当i=k+1,循环终止,因为前k个都为T,如果令k=n,则它把整个Q选出来,显然是正确地。
下面是我在工作中使用的代码,它很少很简单,只有有三个语句。
hand <- FALSE
for (i in 1 : length(carrierNameList)) {
hand <- as.character(rate$Carrier) == getCode(carrierNameList[i]) | hand
}
rate <- rate[which(hand), ]
carrierNameList是一个序列(向量),rate是一个数据框,getCode是一个函数,它负责把carrierNameList转化为可以和rate中的Carrier变量匹配的序列。它总能对任意的carrierNameList(唯一的条件是每个元素经过getCode转化后为rate的carrier变量的一个水平)从rate中选择出所有的记录(行),然后产生新的数据框。你只需要把getCode函数拿掉,换成你的数据,就能解决你的数据面临的同样问题。唯一的不美的地方是它不是一函数的形式(它在逻辑上已经是一个函数了),有志的小伙伴可以试着把它写成一个 R 包。
其中的思想是,使用一个当前向量(伦家还没想好名字)去记录数据框中检查变量对匹配序列情况。首先匹配序列的每个元素和数据框的检验变量进行“==”运算,把这个结果称为判断向量,遍历序列后产生一个判断矩阵。然后对判断向量进行这样的求并:第一个判断向量与逻辑零向量(全为FALSE的逻辑向量)的并为当前向量,如果还有未进行并操作的判断向量,把当前向量与下一个判断向量的并为当前向量(类似于求和算法)。当不剩下未进行并操作的判断向量是,匹配的信息就凝结在当前向量上。最后,按当前向量中为真的下标去选取行。
祝小伙伴们在处理数据的工作上能顺心如意。有错误的地方欢迎提出来。
这里是分割线
从这里更新。
subset函数是行得通的。任何我能想到的一定有人比我前想到了,前面写的东西一无是处。
subset函数的第二个参数是一个二值的(逻辑)向量实例,只要找到满足条件的二值向量,就可以使用subset函数。为了找到这样的向量,使用的工具是正则表达式。
grepl('[a-z]', letters)
# 一个24个TRUE的向量
![](https://img.haomeiwen.com/i11465070/447ae10e4dbd33eb.png)
![](https://img.haomeiwen.com/i11465070/9f6aa818105b254a.png)