使用多个文件在 R 中对值进行分箱

Binning values in R with multiple files

所以我在将多个文本文件中包含的值合并到设定范围内时遇到了一个小问题。我在网上查看了各种包并遇到了可以对值进行分类的 sm,您也可以像这样指定分类范围:-

xb <- binning(x, breaks=seq(-4,4,by=0.5))

但是我在实现它时遇到了一些问题。我无法指定上限,因为我不知道上限是多少(文件有从仪器获得的数千个值),我有 25 个文件,每个文件包含我需要全部装箱的数千个值同时(然后我需要随后取所有这些分箱值的中值)并且我在开始阅读文本文件时遇到了麻烦。我执行:-

read.table("foobar.txt", sep=",")

只读取一个文本文件,因为所有值都用“,”分隔,但它似乎无法处理这个问题。此外,我希望能够将给定范围 0.0005 的值进行分箱(例如,值分箱在 200.0000 - 200.0005、200.0005 - 200.0010 之间,依此类推)

我使用的文本文件是 .txt,其值以逗号分隔并包含数千个值。我的想法是,我将这些值分组到一个设定范围内的组,然后取代表该特定 bin 的这些值的中值。例如,如果我的值是 1,1,2,3,3,4,5,5,6,7,7,9,10 并且我将 bin 的范围设置为 2,那么第一个 bin 将包含1,1,2,第二个 3,3,4,第三个 5,5,6 等,所有这些值的中值被用来表示 bin,第一个 bin 的中值为 1,第二个 3,第三个 5(我知道在这个例子中,取中值似乎毫无意义,但根据我掌握的数据,它是有意义的)

有没有一种方法可以读取多个值的文本文件并按照我描述的方式同时处理它们?有没有这样的包,我可以只看手册?任何建议或提示将不胜感激!

有多种方法可以实现,我将提供一种使用基本函数的方法。 (另一种方法是使用 dplyr,也很适合这种情况。但是,基本示例应该足够简单。)

生成数据

(这里只是因为我们没有您的任何数据。)

n <- 10
for (ii in 1:3) {
    dat <- runif(n)
    writeLines(paste(dat, collapse = ','),
               con = sprintf('user2062207-file%s.txt', ii))
}
readLines('user2062207-file1.txt')
## [1] "0.929472318384796,0.921938128070906,0.707776406314224,0.236701443558559,0.271322417538613,0.388766387710348,0.422867075540125,0.324589917669073,0.92406965768896,0.171326051233336"

读取数据

假设您有一个简单的文件查找模式,您将从这里开始。

fnames <- list.files(pattern = 'user2062207-file.*.txt')
allData <- unlist(sapply(fnames, read.table, sep = ','))
allRange <- range(allData)
df <- data.frame(x = allData)
head(df)
##           x
## 1 0.9294723
## 2 0.9219381
## 3 0.7077764
## 4 0.2367014
## 5 0.2713224
## 6 0.3887664
dim(df)
## [1] 30  1

设置垃圾箱

下面的{floor,ceiling} +/- binSize是因为分箱只包括范围的一侧(默认:右侧),所以最小值不会分箱。它还确保垃圾箱位于圆形边界上。

binSize <- 0.05
allBins <- seq(floor(allRange[1] / binSize) * binSize,
               ceiling(allRange[2] / binSize) * binSize,
               by = binSize)
## bin the data
df$bin <- cut(df$x, breaks = allBins)
head(df)
##           x        bin
## 1 0.9294723 (0.9,0.95]
## 2 0.9219381 (0.9,0.95]
## 3 0.7077764 (0.7,0.75]
## 4 0.2367014 (0.2,0.25]
## 5 0.2713224 (0.25,0.3]
## 6 0.3887664 (0.35,0.4]

每个 Bin 的统计数据

sapply(levels(df$bin), function(lvl) median(df$x[df$bin == lvl], na.rm = TRUE))
##   (0,0.05] (0.05,0.1] (0.1,0.15] (0.15,0.2] (0.2,0.25] (0.25,0.3] (0.3,0.35] 
## 0.03802277         NA 0.11528715 0.18195392 0.22918094 0.27132242 0.33626971 
## (0.35,0.4] (0.4,0.45] (0.45,0.5] (0.5,0.55] (0.55,0.6] (0.6,0.65] (0.65,0.7] 
## 0.38009637 0.42184059         NA 0.53826028 0.57820253 0.64165116 0.67825992 
## (0.7,0.75] (0.75,0.8] (0.8,0.85] (0.85,0.9] (0.9,0.95]   (0.95,1] 
## 0.74243926         NA 0.80759621 0.88974267 0.92406966 0.95691077 

在这个领域,许多其他选择可能是有利的。例如,基本函数 by 可以工作,尽管处理它的数据结构并不总是直观的,即使函数调用本身很容易阅读:

head(by(df$x, df$bin, median, na.rm = TRUE))
## df$bin
##   (0,0.05] (0.05,0.1] (0.1,0.15] (0.15,0.2] (0.2,0.25] (0.25,0.3] 
## 0.03802277         NA 0.11528715 0.18195392 0.22918094 0.27132242 

您也可以轻松使用dplyr。这个例子以原来的allDataallBins开头:

library(dplyr)
data.frame(x = allData) %>%
    mutate(bin = cut(x, breaks = allBins)) %>%
    group_by(bin) %>%
    summarise(median(x))
## Source: local data frame [17 x 2]
##           bin  median(x)
## 1    (0,0.05] 0.03802277
## 2  (0.1,0.15] 0.11528715
## 3  (0.15,0.2] 0.18195392
## 4  (0.2,0.25] 0.22918094
## 5  (0.25,0.3] 0.27132242
#### ..snip..

第一个示例保留空箱,而其他方法不知道空箱。可能还有其他使用 bydplyr 的方法可以包含这些空容器,但这似乎就足够了。

编辑

经过一番交谈,确定数据范围太宽,bin 宽度为 0.0005。设计了一个更好的解决方案。 (没有样本数据提供,抱歉,不是我给的...)我将使用随机数据来模拟这个过程:

set.seed(42)
x <- 5e7 * runif(5e5)

library(dplyr)
binSize <- 0.0005
df <- data.frame(dat = sort(x))
df$bin <- floor(df$dat / binSize) * binSize
head(df)
##         dat       bin
## 1  410.9577  410.9575
## 2  456.6275  456.6270
## 3  552.3674  552.3670
## 4  875.4898  875.4895
## 5 1018.6806 1018.6805
## 6 1102.2436 1102.2435
system.time(results <- df %>% group_by(bin) %>% summarize(med = median(dat)))
##    user  system elapsed 
##   12.08    0.00   12.11 
head(results)
## Source: local data frame [6 x 2]
##         bin       med
## 1  410.9575  410.9577
## 2  456.6270  456.6275
## 3  552.3670  552.3674
## 4  875.4895  875.4898
## 5 1018.6805 1018.6806
## 6 1102.2435 1102.2436