【问题标题】:Mapply with data.frame/list as the Arguments for the Function使用 data.frame/list 作为函数的参数进行映射
【发布时间】:2018-11-20 21:46:00
【问题描述】:

简而言之,我有一个更大的函数,它创建的 data.frames 是一个更大的 data.frame 的子集,并以函数的参数命名。它正在为原始数据以及 Holt-Winters 的输出和预测输出构建 data.frames……这意味着它正在创建多个 data.frames。一个小例子如下(虽然这里没有足够的时间间隔来实际生成一个 ts 类 data.frame):

Group <- c("Primary_Group","Primary_Group","Primary_Group","Primary_Group","Primary_Group","Primary_Group","Secondary_Group","Secondary_Group","Secondary_Group","Secondary_Group","Secondary_Group","Secondary_Group","Tertiary_Group","Tertiary_Group","Tertiary_Group","Tertiary_Group","Tertiary_Group","Tertiary_Group")
Day <- c(1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3)
Type <- c("A","A","A","B","B","B","A","A","A","B","B","B","A","A","A","B","B","B")
Value <- c(7,3,10,3,9,4,0,9,3,10,1,6,3,4,10,2,3,1)
df <- as.data.frame(cbind(Group,Day,Type,Value))

Fun <- function(Group,Type, A, B, G){
    df <- Data[Data$Group== Group & Data$Type== Type, ]
    assign(paste(Group,Type,"_df",sep = ''), df, envir = parent.frame()) 
    df_holtwinters <- HoltWinters(ts(Data[Data$Group== Group & Data$Type== Type, ], 
                                  frequency = 365), alpha = A, beta = B, gamma = G)
    assign(paste(Group,Type,"_hw",sep = ''), df_holtwinters, envir = parent.frame()) 
}

您会注意到 GroupType 是字符,而 A、B、G 是数字或 NULL

如果我现在有一个由列表值组成的 data.frame,我怎样才能最好地循环上述函数(可能使用 mapply)以使用第一行中每一列的值......然后是第 2 行中的每一列等 - 创建几个数据框。

argGroup <- c("Primary_Group","Primary_Group","Secondary_Group","Secondary_Group","Tertiary_Group","Tertiary_Group")
argType <- c("A","B","A","B","A","B")
argA <- c(NA, NA, NA, NA, NA, NA)
argB <- c(0.05, 0.05, NA, NA, NA, NULL)
argG <- c(NA, NA, NA, NA, NA, NA)

argGroup[is.na(argGroup)] <- list(NULL)
argType[is.na(argType)] <- list(NULL)
argA[is.na(argA)] <- list(NULL)
argB[is.na(argB)] <- list(NULL)
argG[is.na(argG)] <- list(NULL)

Arguments <- cbind(argType, argType, argA, argB, argG)

理想情况下,我会生成以下 data.frames...

Primary_Group_A_df
Primary_Group_A_hw
Primary_Group_B_df
Primary_Group_B_hw
Secondary_Group_A_df
Secondary_Group_A_hw
Secondary_Group_B_df
Secondary_Group_B_hw
Tertiary_Group_A_df
Tertiary_Group_A_hw
Tertiary_Group_B_df
Tertiary_Group_B_hw

了解如何最好地(最自动化的方式)rbind 将所有 _df 和所有 _hw 放在一起也会很有帮助。

任何帮助都将是惊人的,非常感谢。非常感谢!

【问题讨论】:

  • Parfait,将列表设置为 NULL 的原因是为了让我可以拥有一个 data.frame,其中的列表包含两个数值(比如 0.05)但也有 NULL(不是 NA)作为值.简而言之,Holt-Winters 为您提供了写入 alpha = 0.05(数值)或 alpha = NULL 的选项,其中 alpha 刚刚优化。我可以将此线程简化为一个更简单的问题:如何使用 mapply 将多列 data.frame 应用于函数,以便它循环遍历每一行的参数(意味着第 1 行中的每一列都是迭代一的参数。 ..然后第二行...等)?

标签: r


【解决方案1】:

避免使用许多类似结构的对象淹没您的全局环境。考虑使用诸如列表之类的容器来保存许多数据帧。一种有用的方法是by 按一个或多个因素(例如 GroupType)对数据帧进行子集化,以返回数据帧列表。此外,不要按行迭代,而是 merge 参数与每个子集的一次参数传递的数据。

具体来说,为 dfhw 列表调用两次by。但首先,按 GroupType 合并 dfArguments 数据框。一个挑战是NULL 不能存储在数据框中,因此请考虑保存"NULL" 字符串并分配临时变量以传递给HW 参数。不幸的是,这会将整个列转换为字符类型,您需要将其转换为 as.numeric 以获得非 NULL 值。

合并

Group <- c("Primary_Group","Primary_Group","Secondary_Group","Secondary_Group",
           "Tertiary_Group","Tertiary_Group")
Type <- c("A","B","A","B","A","B")
argA <- c("NULL", "NULL", "NULL", "NULL", "NULL", "NULL")
argB <- c(0.05, 0.05, "NULL", "NULL", "NULL", "NULL")
argG <- c("NULL", "NULL", "NULL", "NULL", "NULL", "NULL")

Arguments <- data.frame(Group, Type, argA, argB, argG, stringsAsFactors=FALSE)
df <- merge(df, Arguments, by=c("Group", "Type"))

数据框列表 (带有命名的df元素)

# ORDER FOR NAMING LATER
df <- with(df, df[order(Type, Group),])

# DATAFRAME LIST
df_list <- by(df, df[c("Group", "Type")], identity)
# RENAME LIST
df_list <- setNames(df_list, unique(paste0(df$Group, "_", df$Type, "_df")))

# REFERENCE ELEMENTS
df_list$Primary_Group_A_df
df_list$Secondary_Group_A_df
df_list$Tertiary_Group_A_df
...

硬件列表 (带有命名的硬件元素)

# HW LIST
hw_list <- by(df, df[c("Group", "Type")], function(sub) {
  # CONDITIONALLY ASSIGN TEMP VARIABLES 
  # (BEING SUBSETS: max(arg*)==min(arg*)==mean(arg*)==median(arg*))
  if(!is.na(max(sub$argA)) & max(sub$argA) == "NULL") { tmpA <- NULL } 
  else { tmpA <- max(as.numeric(sub$argA)) }

  if(!is.na(max(sub$argB)) & max(sub$argB) == "NULL") { tmpB <- NULL } 
  else { tmpB <- max(as.numeric(sub$argB)) }

  if(!is.na(max(sub$argG)) & max(sub$argG) == "NULL") { tmpG <- NULL } 
  else { tmpG <- max(as.numeric(sub$argG)) }

  # PASS ARGS ONCE PER SUBSET 
  return(HoltWinters(ts(sub, frequency = 365), alpha=tmpA, beta=tmpB, gamma=tmpG))
})

# RENAME LIST
hw_list <- setNames(hw_list, unique(paste0(df$Group, "_", df$Type, "_hw")))

# REFERENCE ELEMENTS
hw_list$Primary_Group_A_hw
hw_list$Secondary_Group_A_hw
hw_list$Tertiary_Group_A_hw
...

输出 (使用 3 表示 HW 的频率以与发布的数据对齐)

> hw_list$Primary_Group_A_hw
Holt-Winters exponential smoothing with trend and additive seasonal component.

Call:
HoltWinters(x = ts(sub[c("Group", "Day", "Type", "Value")], frequency = 3),     alpha = tmpA, beta = tmpB, gamma = tmpG)

Smoothing parameters:
 alpha: 0.2169231
 beta : 0.05
 gamma: 0.1

Coefficients:
          [,1]
a   2.89129621
b   0.08783715
s1  0.54815382
s2 -0.12485260
s3  0.21087038

> hw_list$Secondary_Group_A_hw
Holt-Winters exponential smoothing with trend and additive seasonal component.

Call:
HoltWinters(x = ts(sub[c("Group", "Day", "Type", "Value")], frequency = 3),     alpha = tmpA, beta = tmpB, gamma = tmpG)

Smoothing parameters:
 alpha: 0.752124
 beta : 0
 gamma: 0

Coefficients:
            [,1]
a   3.691664e+00
b   3.333333e-01
s1  3.333333e-01
s2 -1.480388e-16
s3 -3.333333e-01

> hw_list$Tertiary_Group_A_hw
Holt-Winters exponential smoothing with trend and additive seasonal component.

Call:
HoltWinters(x = ts(sub[c("Group", "Day", "Type", "Value")], frequency = 3),     alpha = tmpA, beta = tmpB, gamma = tmpG)

Smoothing parameters:
 alpha: 0.3145406
 beta : 0
 gamma: 0

Coefficients:
            [,1]
a   3.022946e+00
b  -3.333333e-01
s1 -3.333333e-01
s2 -1.480388e-16
s3  3.333333e-01

【讨论】:

  • 优秀。非常有帮助和非常好的建议。
【解决方案2】:

使用as.data.frame(cbind(...)) 会丢失类型信息, 直接使用data.frame

Data <- data.frame(
  Group = rep(c("Primary_Group", "Secondary_Group", "Tertiary_Group"), each = 6L),
  Day = rep(1L:3L, 6L),
  Type = rep(rep(c("A", "B"), each = 3L), 3L),
  Value = c(7,3,10,3,9,4,0,9,3,10,1,6,3,4,10,2,3,1)
)

之后,我认为您可以执行以下操作:

split_data <- split(Data, as.list(Data[, c("Group", "Type")]))
dfs <- do.call(rbind, split_data)

dfs_hw <- lapply(split_data, function(sub_data) {
  Map(argA, argB, argG, f = function(A, B, G) {
    HoltWinters(ts(sub_data, frequency = 365), alpha = A, beta = B, gamma = G)
  })
})

dfs_hw <- do.call(rbind, unlist(dfs_hw, recursive = FALSE))

但我收到来自HoltWinters 的错误, 所以我不能肯定地说。 另外,我认为dfs 又是Data,只是重新排序。

【讨论】:

  • 仍然有多少用户忽略了by,它取代了split + lapply
  • 花式。我现在不明白为什么我以前从未见过它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-19
  • 1970-01-01
  • 2018-08-03
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多