我正在研究多元时间序列数据。为了测试平稳性,例如使用 adf.test 等,我需要为每个变量及其差异获取一堆单向量时间序列,而不是原始的 data.frame。我学习了以下代码:
dif1<- c()
for(i in 1:length(city)){
dif1[i]= paste(city[i],"diff1",sep=".")
}
for(x in 1:22){
assign(paste(dif1[x]), diff((eval(parse(text = city[x])))[,1]))
}
结果是这样的:
> dif1
[1] "AZ.Phoenix.diff1" "CA.Los.Angeles.diff1" "CA.San.Diego.diff1" "CA.San.Francisco.diff1" "CO.Denver.diff1"
[6] "DC.Washington.diff1" "FL.Miami.diff1" "FL.Tampa.diff1" "GA.Atlanta.diff1" "IL.Chicago.diff1"
[11] "MA.Boston.diff1" "MI.Detroit.diff1" "MN.Minneapolis.diff1" "NC.Charlotte.diff1" "NV.Las.Vegas.diff1"
[16] "NY.New.York.diff1" "OH.Cleveland.diff1" "OR.Portland.diff1" "TX.Dallas.diff1" "WA.Seattle.diff1"
[21] "Composite.20.diff1" "National.US.diff1"
我对上面的代码如何工作感到非常困惑。例如,为什么分配行中需要索引“[,1]”?我尝试删除它,结果发现所有单个向量都变成了空。谁能帮我理解上面的代码是如何工作的?谢谢。
dif1 <- c()
for(i in 1:length(city)){
dif1[i]= paste(city[i],"diff1",sep=".")
}
在这里,您将迭代
city
向量(一个字符向量,其值“AZ.Phoenix”一直到“National.US”,并在每个值的末尾添加“.diff”)中的每个值。结果存储在 dif1
中,它现在是一个包含 22 个元素“AZ.Phoenix.diff”...“National.US.diff”的字符向量。
然后我们有疯狂的部分:
for(x in 1:22){
assign(paste(dif1[x]), diff((eval(parse(text = city[x])))[,1]))
}
让我们从内到外看看。
parse(text = city[x])
字符串之间的 R 存在差异:
"print(5 + 5)"
和实际代码:
print(5 + 5)
使用
parse
,您可以转换字符串,使其实际上被视为未计算的表达式。所以
parse(text = "print(5 + 5)")
将转换为:
expression(print(5 + 5))
要真正拥有该表达式 run,您需要使用
eval
: 对其进行评估
eval(parse(text = "print(5+5)")
实际看到“10”被打印出来。在你的情况下
eval(parse(text = city[x]))
当
x
值为1时,基本上会被R视为代码:
AZ.Phoenix.diff1
我认为你可能有一组额外的括号,它们并没有真正做任何事情,这让我们:
(eval(parse(text = city[x])))
所以,让我们再次看一下代码,就好像我们没有执行任何 for 循环,只是使用
x
= 1,这样 city[x]
就是 "AZ.Phoenix"
,dif1[x]
就是 "AZ.Phoenix.diff1"
。
assign(paste("AZ.Phoenix.dif1"), diff(AZ.Phoenix[,1]))
我认为围绕单个角色的
paste
调用没有做任何事情,所以我很确定应该可以删除它:
assign("AZ.Phoenix.dif1", diff(AZ.Phoenix[,1]))
我可能会误解,但这看起来您的环境中已经存在一个名为 AZ.Phoenix 的数据框。
[,1]
是一个索引选项,您可以将其解读为“所有行,仅第一列”。一般来说,对于 R 中的任何 data.frame 或矩阵,您可以通过调用 my_dataframe[the rows to keep, the columns to keep]
来挑选特定的行和列。
现在我们到了
diff()
,这是一个可以将像2 4 6 8 5
这样的序列转换为2 2 2 -3
的函数。更多信息请参见:https://www.r-bloggers.com/2023/06/mastering-the-power-of-rs-diff-function-a-programmers-guide/。
它是您的时间序列转换为每个值之间的一系列差异。
最后,我们到达了
assign()
。下面的两段代码将做同样的事情:
# Without assign:
my_number <- 5
# With assign:
assign("my_number", 5)
我认为您应该拥有理解您发布的代码中发生的所有事情所需的所有内容。
您的代码正在使用
parse
、eval
和 assign
的强大功能在循环中创建新变量。这样做的一个限制是代码(就像您发布的代码)最终很难遵循。但也许更大的限制是,如果不深入更多 parse
和 eval
代码行,您就无法继续以自动化方式创建的变量。
例如,如果您想将创建的每个向量乘以 1000 来更改单位,该怎么办?您需要手动执行:
AZ.Phoenix.diff1 <- 1000 * AZ.Phoenix.diff1
CA.Los.Angeles.diff1 <- 1000 * CA.Los.Angeles.diff1
以此类推,22次。如果您添加更多城市,这将非常粗糙,并且需要手动重新调整。
了解
lapply()
。修改后的代码可能看起来类似于:
# A list of all your timeseries, defined once at the start
city_timelines <- list(
AZ.Phoenix,
CA.Los.Angeles,
# rest of cities here
)
city_diffs <- lapply(
city_timelines,
function(x) {
diff(x)
}
)
如果您想随后对代码执行更多操作::
# Change units (multiply every series by 1000):
city_diffs_new_units <- lapply(
city_diffs,
function(x) {
1000 * x
}
)
# Access series by name
names(city_diffs_new_units) <- city
city_diffs_new_units$"AZ.Phoenix" # Will spit out the AZ.Phoenix series
# Or by number
city_diffs_new_units[[1]] # Will also spit out the AZ.Phoenix series
lapply()
和其他 apply()
函数一开始可能看起来很难学,但它们有一个非常清晰的逻辑,可以在许多编程语言中使用,并且许多其他 R 程序员将更容易能够工作拥有并理解比 eval/parse/assign
的东西。