i=1
x&i=3
x&i就是动态变量。
如果x&i按照我们所想的一样,那么上述程序的结果应该是得到了一个变量x1=3
上述程序在VBA中可直接实现。
动态变量可以用于批量变量迭代,这是动态变量最重要的意义。如:
for(i in 1:100){
x&i=i
}
这是时候我们快速地生成了x1:x100这100个变量,并进行了赋值。如果x&i的作用按照我们想的一样实现了的话,那么上述程序应该生成了x1=1,x2=x,x3=3...x100=100
所以问题的核心就是x&i的实现,核心的核心就是&的含义。
在SAS中,我记得连接需要加一个英文句号:
x&.i=3
在VBA中,可直接用
x&i=3
然而在R与Python要复杂得多。
有一篇写的很好的博客,参见Python动态变量名定义与调用
字典方法是Stack Overflow社区最推崇的方法,但是它不能适用于对复杂变量进行进一步的操作。它不能解决字典的value对变量名的引用。
d={}
for i in range(5):
d['x'+str(i)]=i
那么现在我们把问题复杂化一点,假设我已经有了x1-x4这么几个变量,他们都是列表。我想只留下非0的值,那么理想的伪代码如下:
x1=np.array([1,2,3,0])
x2=np.array([5,6,0,8])
x3=np.array([3,5,0,0])
x4=np.array([3,45,2,1])
for i in range(1,5):
xi=xi[xi!=0]
那么字典方法应该怎么做呢?首先应该将变量全部写到字典里面去,,,然后发现首先这一步就没法做,它不能解决字典的value对变量名的引用,就是在值里面无法引用动态变量。
你打算这么做吗?
d['x'+str(i)]='x'+str(i)
可是右边只返回了一个字符串,不指代xi。
Python中有三个函数是变量字典:locals(),globals, vars()。
他们之间的区别参见:What's the difference between globals(), locals(), and vars()?
一般使用locals,他会返回当前存在的变量,返回格式是一个字典。也就是说,如果我们先定义x1=2,那么在locals字典中就已经记录了下来,使用
loacals()['x1']
就可以引用变量x1了,这就是一个普通字典格式,dict['key']=value。
好我们继续上面的问题,现在就可以很方便地解决了
x1=np.array([1,2,3,0])
x2=np.array([5,6,0,8])
x3=np.array([3,5,0,0])
x4=np.array([3,45,2,1])
for i in range(1,5):
locals()['x'+str(i)]=locals()['x'+str(i)][locals()['x'+str(i)]!=0]
print(x1,x2,x3,x4)
[1 2 3] [5 6 8] [3 5] [ 3 45 2 1]
在Python中同样有三个函数可以编译执行字符串语句:exec,eval,compile,详细地讲解参见
python中的exec()、eval()以及complie()
What's the difference between eval, exec, and compile?
eval('1+2')#返回3
exec('1+2')#无返回值
exec可同时解析字符串和python语句,但exec的输入必须全是字符串格式。下面的语句定义了x1=2:
exec('x'+str(1)+'='+str(2))#全是字符串,以+连接
虽然stack overflow上面最推崇字典法,而认为命名空间法不是一个很好的办法,因为他改变了全局变量,比如如果你之前有某个变量定义为x23,你在动态变量里面改变了x23的赋值,你可能没有注意到这一点,这可能会造成意料之外的错误。但是我认为,命名空间法非常的便捷,并且很多复杂的数据结构的时候,命名空间法师唯一可行的方法。
我最爱的R在这里确实最复杂的,&在R中是逻辑运算“和”,并不表示连接。
一般有五种方法能够实现变量批量命名:
my_power_list=setNames( as.list( 1:5), paste0("x", 1:5) )
效果是这样的:
这实际上用了一个列名重命名技术而已。而且只在列表中,效果很有限。
x =as.list(rnorm(100))
names(x) = paste("a", 1:100, sep = "")#列表重命名
list2env(x , envir = .GlobalEnv)#将列表变量转换为全局变量
vals <- rnorm(3)
n <- length(vals)
lhs <- paste("a", 1:n, sep="")
rhs <- paste("vals[",1:n,"]", sep="")
eq <- paste(paste(lhs, rhs, sep="<-"), collapse=";")
eval(parse(text=eq))
其本质是生成了一段文本,然后用parse去解析,在用eval进行符号运算。
上面的三种并不是很常用,我比较喜欢下面两种:
assign(paste0("x", 1:100), 1:100)
这一次性生成了100个变量:xi=i。
上面已经讲到,动态变量最重要的意义是能够利用迭代对变量进行批量运算。很遗憾,仅仅凭借上面的函数是无法实现迭代的。我们需要另外一个函数:get()。get函数和assign是一对,assign给字符赋值,get获得字符的值。
现在看一个实例。假定有一个数据集d是100行30列的,也就是说有30个变量,现在用bootstrap收集它的系数。进行100次随机抽样,每一次抽样用第一列数据对其他列进行线性回归,这样,每一次回归都会得到一次beta0~beta29,一共30个beta(还有个截距项beta0)。进行100次抽样后再回归就得到了100个不同的beta0,把这些beta0全部追加到变量beta0中。然后进行统计量的估计。也就是说,最后我们的结果是:。
d=matrix(rnorm(100*30),100,30)
d=data.frame(d)
cs=paste0('beta',1:30)
for(k in 1:30){
assign(cs[k],c())
}#要追加必给初值
for(j in 1:100){
i=sample(1:100,80,replace = F)
d1=d[i,]
model=lm(X1~.,data=d1)
for(k in 1:30){
assign(cs[k],c(get(cs[k]),coef(model)[k]))
}
}
如果不进行动态变量引用,那么我们程序可能是这样滴:
d=matrix(rnorm(100*30),100,30)
d=data.frame(d)
beta0=c()
beta1=c()
beta2=c()
beta3=c()
beta4=c()
beta5=c()
beta6=c()
beta7=c()
...
beta30=c()
for(j in 1:100){
i=sample(1:100,80,replace = F)
d1=d[i,]
model=lm(X1~.,data=d1)
beta0=c(beta0,coef(model)[1])
beta1=c(beta1,coef(model)[2])
beta2=c(beta2,coef(model)[3])
beta3=c(beta3,coef(model)[4])
beta4=c(beta4,coef(model)[5])
beta5=c(beta5,coef(model)[6])
beta6=c(beta6,coef(model)[7])
beta7=c(beta7,coef(model)[8])
...
beta30=...
}
现在你知道了动态引用的重要意义了吧,没错,几十倍地精简代码!对变量进行批量迭代!
再看最后一种方法。
这种方法其实是最符合x&i定义的一种方法。
e=globalenv()
name=paste0('x',1:5)
e[[name[1]]]=3
似乎有点像Python的locals,但是注意:
使用这种方法进行bootstrap:
e=globalenv()
cs=paste0('beta',1:30)
for(k in 1:30){
assign(cs[k],c())
}
for(j in 1:100){
i=sample(1:100,80,replace = F)
d1=d[i,]
model=lm(X1~.,data=d1)
for(k in 1:30){
e[[cs[k]]]=c(e[[cs[k]]],coef(model)[k])
}
}