【问题标题】:Transferring factor properties between two data frames在两个数据帧之间传递因子属性
【发布时间】:2014-05-28 17:45:07
【问题描述】:

我已经建立了一个使用大量(30 个左右)自变量变量的预测模型。由于我使用的数据集比我机器的 RAM 大得多,因此我已经为我的训练集和测试集对其进行了采样。

我现在希望使用该模型对整个数据集进行预测。我一次提取 100 万行数据集,每次我都会为我的一些因子变量找到新的水平,这些变量不在我的训练和测试集中,因此阻止了模型进行预测。

由于有如此多的独立因素变量(以及如此多的整体观察结果),手动纠正每个案例变得非常痛苦。

另外一个需要注意的问题是:不能保证整个数据帧和训练/测试集中的变量顺序是相同的,因为我对改变它们顺序的数据进行了预处理。

因此,我想编写一个函数:

  1. 根据新数据的列选择和排序 我的采样数据框的配置
  2. 遍历采样数据帧和新数据帧,并指定新数据帧中的所有因子水平 在相应列中不存在的数据框 示例数据框为Other
  3. 如果我的样本中存在因子水平但新数据框中不存在因子水平,则在新数据框中的相应列中创建水平(未分配观察值)。

我有 #1 在一起,但不知道做 #2 和 #3 的最佳方法。如果是其他语言,我会使用 for 循环,但我知道这在 R 中是不受欢迎的。

这是一个可重现的例子:

sampleData <- data.frame(abacus=factor(c("a","b","a","a","a")), montreal=factor(c("f","f","f","f","a")), boston=factor(c("z","y","z","z","q")))
dataset <- data.frame(florida=factor(c("e","q","z","d","b", "a")), montreal=factor(c("f","f","f","f","a", "a")), boston=factor(c("m","y","z","z","r", "f")), abacus=factor(c("a","b","z","a","a", "g")))

sampleData
  abacus montreal boston
1      a        f      z
2      b        f      y
3      a        f      z
4      a        f      z
5      a        a      q

dataset
  florida montreal boston abacus
1       e        f      m      a
2       q        f      y      b
3       z        f      z      z
4       d        f      z      a
5       b        a      r      a
6       a        a      f      g

sampleData <- sample[,order(names(sampleData))]
dataset <- dataset[,order(names(dataset))]
dataset <- dataset[,(colnames(sampleData)]

下面是我希望dataset 在此功能完成后的样子(我并不真正关心dataset 中列的最终顺序;我只是认为它对于循环是必要的(或任何你们认为最好的)来工作。请注意,dataset$florida 列被省略:

dataset
  montreal boston abacus
1   f      Other  a
2   f      y      b
3   f      z      Other
4   f      z      a
5   a      Other  a
6   a      Other  Other

另请注意,在dataset 中,boston 的“q”级别不会出现,尽管它确实出现在 sampleData 中。因此,如果我们在dataset 的因子中省略“q”,则水平会有所不同,这意味着在“数据集”中,我们需要boston 来包含水平q,但没有分配给它的实际观察值。

最后,请注意,由于我一次对 30 个变量执行此操作,因此我需要一种编程解决方案,而不是使用显式列名重新分配因子的解决方案。

【问题讨论】:

  • 务实地说,如果你随机抽样 1e6 行并且一个因素只是偶尔出现,那么它对训练 methinks 是没有用的。除非您进行异常值检测,否则您最好将它们过滤掉??
  • 我已经在训练/测试集上完成了这项工作。但是,现在我在整个数据集上进行预测,其中包括这些不常出现的因子变量。

标签: r dataframe


【解决方案1】:

这似乎可行。

通过此函数,boston 列返回的新级别为 Other y z q,即使级别 q 没有值。关于您在原始问题中的评论,我发现有效应用新因子水平的唯一方法也是像您一样使用for 循环,到目前为止它对我来说效果很好。

一个函数findOthers()

findOthers <- function(newData)  ## might want a second argument for sampleData
{
      ## take only those columns that are in 'sampleData'
    dset <- newData[, names(sampleData)]
      ## change the 'dset' columns to character
    dsetvals <- sapply(dset, as.character)
      ## change the 'sampleData' levels to character
    samplevs <- sapply(sampleData, function(y) as.character(levels(y)))
      ## find the unmatched elements
    others <- sapply(seq(ncol(dset)), function(i){
        !(dsetvals[,i] %in% samplevs[[i]])
    })
      ## change the unmatched elements to 'Other'
    dsetvals[others] <- "Other"
      ## create new data frame
    newDset <- data.frame(dsetvals)
      ## get the new levels for each column
    newLevs <- lapply(seq(newDset), function(i){
        Get <- c(as.character(newDset[[i]]), as.character(samplevs[[i]]))
        ul <- unique(unlist(Get))
    })
      ## set the new levels for each column
    for(i in seq(newDset)) newDset[,i] <- factor(newDset[,i], newLevs[[i]])
      ## result
    newDset
}

您的样本数据:

sampleData <- data.frame(abacus=factor(c("a","b","a","a","a")), 
                         montreal=factor(c("f","f","f","f","a")), 
                         boston=factor(c("z","y","z","z","q")))
dataset <- data.frame(florida=factor(c("e","q","z","d","b", "a")), 
                      montreal=factor(c("f","f","f","f","a", "a")), 
                      boston=factor(c("m","y","z","z","r", "f")), 
                      abacus=factor(c("a","b","z","a","a", "g")))

致电findOthers()查看新因子水平的结果:

(new <- findOthers(newData = dataset))
#   abacus montreal boston
# 1      a        f  Other
# 2      b        f      y
# 3  Other        f      z
# 4      a        f      z
# 5      a        a  Other
# 6  Other        a  Other

as.list(new)
# $abacus
# [1] a     b     Other a     a     Other
# Levels: a b Other
# 
# $montreal
# [1] f f f f a a
# Levels: f a
# 
# $boston
# [1] Other y     z     z     Other Other     
# Levels: Other y z q     ## note the new level 'q', with no value in the column

【讨论】:

  • 有趣。只是为了测试它,您能否在原始“样本数据”数据帧(例如abacus~ montreal+boston)上运行线性回归,看看您是否可以在新数据集数据被您的函数过滤后做出预测? (我会,但我现在远离我的机器)。
  • 我刚刚运行了一个线性模型sampleData 并在家里使用new 进行预测,它似乎有效(需要注意的是,我们需要将'Other' 添加到sampleData 作为一个级别在我的示例中的每一列中——这没问题,因为我的真实样本数据集中已经有'Other'。剩下的一个松散的结局——我看到尽管new 中的因子名称是相同的作为SampleData,他们的级别顺序不同——这会给我错误的结果吗?
  • 可能。我们可以按原样订购它们,然后将新关卡附加到现有关卡并试一试。
  • 您有机会测试您的更改吗?
  • 对不起。我还没有。我现在有期末考试。你能从中得到什么有用的东西吗?
【解决方案2】:

只回答您提出的问题(而不是建议您可能会做什么)。在这里,我们必须制作每一列字符,替换然后重新分解。

sampleData = sapply(sampleData, as.character)
sampleData = gsub("q", "other", sampleData)
sampleData = sapply(sampleData, as.factor)

这取决于“q”只占一列。否则,您只需分别编辑每一列即可获得所需的更改:

sampleData = sapply(sampleData, as.character)
sampleData$boston = gsub("q", "other", sampleData$boston)
sampleData = sapply(sampleData, as.factor)

但是我认为您应该只过滤这些行的训练和测试数据,因为它们太少了 它们对您的模型完全没有影响。否则你就麻烦了。

summary(dataset)
dataset <- dataset[dataset$abacus!="z", ]

如果数据集非常大并且您因此没有这样做,那么您可能希望使用dplyr 包和filter 函数之类的东西来执行此操作。

【讨论】:

  • 不确定这个解决方案是否适合我。我更关心修复dataset 而不是sampleData;更重要的是,sampleData 中的q 可能不会如此频繁地出现——它可能不会出现在我正在拉取的整个数据集的块中——因为我是按顺序拉取它,而不是随机拉取。
  • 另外,由于我正在处理很多因因素变量,手动过滤变量(这是我目前正在做的)是一个非常耗时的过程,我想避免.
【解决方案3】:

这能实现你想要的吗?

# Select and sort the columns of dataset as in sampleData
sampleData <- sampleData[, order(names(sampleData))]
dataset <- dataset[, colnames(sampleData)]

f <- function(dataset, sampleData, col) {
    # For a given column col, assign "Other" to all factor levels 
    # in dataset[col] that do not exist in sampleData[col].
    # If a factor level exists in sampleData[col] but not in dataset[col],
    # preserve it as a factor level.
    v <- factor(dataset[, col], levels = c(levels(sampleData[, col]), "Other"))
    v[is.na(v)] <- "Other"
    v
}

# Apply f to all columns of dataset
l <- lapply(colnames(dataset), function(x) f(dataset, sampleData, x))

res <- data.frame(l) # Format into a data frame
colnames(res) <- colnames(dataset) # Assign the names of dataset
dataset <- res # Assign the result to dataset

可以如下测试

> dataset[, "boston"]
[1] Other y     z     z     Other Other
Levels: q y z Other
> dataset[, "montreal"]
[1] f f f f a a
Levels: a f Other
> dataset[, "abacus"]
[1] a     b     Other a     a     Other
Levels: a b Other

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-31
    • 2017-10-09
    • 1970-01-01
    相关资源
    最近更新 更多