如何根据重复次数将每列中的某些值设置为零?

时间:2015-09-22 17:55:40

标签: r

我有一个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列)?

2 个答案:

答案 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))