我有一个117,000行的数据框,其中10,000列包含值,有些是经常重复而有些则没有。我的目标是将每列中重复次数少于100次的值替换为零。我在这里举了一个小例子: 我的意见是这样的:
1 200 444
2 310 000
3 310 000
4 444 444
5 200 444
6 200 112
7 310 444
8 310 876
9 310 876
10 444 876
我需要在输出中将任何在列内重复少于3次的值设置为零(例如在第1列中,值444重复少于3次,因此应将其设置为零并且相同应该对第2栏中的值000和112进行:
1 200 444
2 310 0
3 310 0
4 0 444
5 200 444
6 200 0
7 310 444
8 310 876
9 310 876
10 0 876
可以通过编写一个脚本在R中为巨大的data.frame执行此操作来帮助我吗? (117000行和10000列)?
答案 0 :(得分:2)
更新以合并@ Arun的评论,并使用更能代表真实案例的数据集进行演示。
这个问题非常有趣,主要是因为完整的数据集非常大(117,000行X 10,000列)。因此,主要问题不是如何标记不太常见的条目,而是如何有效地执行此操作。这显示了三个选项:data.frame选项,naive data.table选项(我的)和复杂的data.table选项(@ Arun's)。这是data.table优越性的一个对象课程,尤其是在正确使用时。
# create sample: 117,000 rows, 100 columns (1% of real case)
set.seed(1) # for reproducibility
ltrs <- sapply(letters,function(x)paste(rep(x,3),collapse=""))
df <- data.frame(replicate(100,sample(ltrs, 117e3, replace = TRUE,
p=c(0.5,0.3,0.1,0.09,rep(0.01/22,22)))),
stringsAsFactors = FALSE)
因此,此数据集包含117,000行和100列(100%的行,1%的列)。每个条目都是三个字母的字符串(“AAA”,“BBB”等)。 AAA - DDD占99%的条目,其余22个字母共占其余1%。因此,必然存在这些频率&lt; 100。
# data.frame option
set.col <- function(x) {
tbl <- table(x)
x[x%in%names(tbl)[tbl<100]]<-NA
x
}
system.time(
result.1 <- as.data.frame(sapply(df,set.col))
)
# user system elapsed
# 44.52 0.27 44.95
因此data.frame选项在~44秒内运行(在我的系统上)。真实案例将在约4400秒或约73分钟内完成。
# naive data.table
library(data.table)
result.2 <- as.data.table(df)
system.time(
for (j in 1:ncol(df)) {
tbl <- table(result.2[[j]])
set(result.2, i=which(result.2[[j]]%in%names(tbl)[tbl<100]),j=j, NA)
}
)
# user system elapsed
# 1.51 0.05 1.56
天真的data.table方法仍然使用table(...)
,但利用了set(...)
。它的运行速度提高了约30倍。完整的数据集将在大约3分钟内运行。
# sophisticated data.table
# install.packages("data.table", type="source") # requires 1.9.6+
library(data.table)
result.3 <- as.data.table(df)
system.time(
for (j in 1:ncol(df)) {
tbl <- result.3[,.N,by=c(names(result.3)[j])][N<100]
result.3[tbl, c(names(result.3)[j]):=NA, on=c(names(result.3)[j])]
}
)
# user system elapsed
# 0.65 0.00 0.67
identical(result.2,result.3)
# [1] TRUE
复杂的data.table解决方案运行速度提高了2.5倍,比数据框选项快了约70倍。完整的数据集应该在大约65秒内运行。
以下只是演示了选项3适用于问题中的示例:
# import sample data
df <- read.table(header=F, row.names=1, colClasses=c("character"),
text="1 200 444
2 310 000
3 310 000
4 444 444
5 200 444
6 200 112
7 310 444
8 310 876
9 310 876
10 444 876")
result.3 <- as.data.table(df)
for (j in 1:ncol(df)) {
tbl <- result.3[,.N,by=c(names(result.3)[j])][N<3]
result.3[tbl, c(names(result.3)[j]):=NA, on=c(names(result.3)[j])]
}
result.3
# V2 V3
# 1: 200 444
# 2: 310 NA
# 3: 310 NA
# 4: NA 444
# 5: 200 444
# 6: 200 NA
# 7: 310 444
# 8: 310 876
# 9: 310 876
# 10: NA 876
要将结果写入csv文件,请使用以下内容:
write.csv(result.3, file="myfile.csv")
答案 1 :(得分:0)
好的,这是一个类似问题的解决方案,与您在简化版中列出的问题相似。评论应该随之解释。
这里我们替换所有出现严格少于四次的元素,因此test_df
的整个第二列应该为零。
# Creates fake test dataframe
col1 <- c(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)
col2 <- c(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
test_df <- data.frame(col1, col2)
# Finds the number of occurences of every element in the dataframe
occurences <- table(unlist(test_df))
# Find the unique elements across the whole dataframe
elements <- unique(unlist(test_df))
# Creates an empty vector for all elements less than four
elements_less_than_four <- c()
# Loops through all elements in the dataframe and if they appear less than
# four times puts them in a list
for(el in elements){
if( occurences[[el]] < 4){
elements_less_than_four <- c(elements_less_than_four, el)
}
}
# Unlist the df for quick comparison, turn all necessary values to zero.
unlisted_df <- as.vector(unlist(test_df))
correct_values <- replace(unlisted_df, unlisted_df %in% elements_less_than_four, 0)
# Reformats the dataframe
finished_df <- data.frame(matrix(correct_values, nrow = nrow(test_df),
ncol = ncol(test_df), byrow = FALSE))