Tôi có một khung dữ liệu và một số cột có NA
giá trị.
Làm cách nào để thay thế các NA
giá trị này bằng số không?
Tôi có một khung dữ liệu và một số cột có NA
giá trị.
Làm cách nào để thay thế các NA
giá trị này bằng số không?
Câu trả lời:
Xem bình luận của tôi trong câu trả lời @ gsk3. Một ví dụ đơn giản:
> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1 4 3 NA 3 7 6 6 10 6 5
2 9 8 9 5 10 NA 2 1 7 2
3 1 1 6 3 6 NA 1 4 1 6
4 NA 4 NA 7 10 2 NA 4 1 8
5 1 2 4 NA 2 6 2 6 7 4
6 NA 3 NA NA 10 2 1 10 8 4
7 4 4 9 10 9 8 9 4 10 NA
8 5 8 3 2 1 4 5 9 4 7
9 3 9 10 1 9 9 10 5 3 3
10 4 2 2 5 NA 9 7 2 5 5
> d[is.na(d)] <- 0
> d
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1 4 3 0 3 7 6 6 10 6 5
2 9 8 9 5 10 0 2 1 7 2
3 1 1 6 3 6 0 1 4 1 6
4 0 4 0 7 10 2 0 4 1 8
5 1 2 4 0 2 6 2 6 7 4
6 0 3 0 0 10 2 1 10 8 4
7 4 4 9 10 9 8 9 4 10 0
8 5 8 3 2 1 4 5 9 4 7
9 3 9 10 1 9 9 10 5 3 3
10 4 2 2 5 0 9 7 2 5 5
Không cần phải áp dụng apply
. =)
BIÊN TẬP
Bạn cũng nên xem qua norm
gói. Nó có rất nhiều tính năng hay để phân tích dữ liệu. =)
df[19:28][is.na(df[19:28])] <- 0
Các tùy chọn lai ghép dplyr hiện nhanh hơn khoảng 30% so với các tập hợp con Base R được gán lại. Trên khung dữ liệu dữ liệu 100M mutate_all(~replace(., is.na(.), 0))
chạy nhanh hơn nửa giây so với d[is.na(d)] <- 0
tùy chọn R cơ sở . Những gì một người muốn tránh cụ thể là sử dụng một ifelse()
hoặc một if_else()
. (Toàn bộ 600 phân tích thử nghiệm đã chạy đến hơn 4,5 giờ chủ yếu là do bao gồm các phương pháp tiếp cận này.) Vui lòng xem các phân tích điểm chuẩn bên dưới để biết kết quả hoàn chỉnh.
Nếu bạn đang vật lộn với các datafram lớn, data.table
là tùy chọn nhanh nhất trong tất cả: nhanh hơn 40% so với cách tiếp cận Base R tiêu chuẩn . Nó cũng sửa đổi dữ liệu tại chỗ, cho phép bạn làm việc với gần gấp đôi số lượng dữ liệu cùng một lúc.
Địa điểm:
mutate_at(c(5:10), ~replace(., is.na(.), 0))
mutate_at(vars(var5:var10), ~replace(., is.na(.), 0))
mutate_at(vars(contains("1")), ~replace(., is.na(.), 0))
contains()
, cố gắng ends_with()
,starts_with()
mutate_at(vars(matches("\\d{2}")), ~replace(., is.na(.), 0))
Có điều kiện:
(chỉ thay đổi một loại và để các loại khác một mình.)
mutate_if(is.integer, ~replace(., is.na(.), 0))
mutate_if(is.numeric, ~replace(., is.na(.), 0))
mutate_if(is.character, ~replace(., is.na(.), 0))
Đã cập nhật cho dplyr 0.8.0: các hàm sử dụng ~
các ký hiệu định dạng purrr : thay thế các đối funs()
số không dùng nữa .
# Base R:
baseR.sbst.rssgn <- function(x) { x[is.na(x)] <- 0; x }
baseR.replace <- function(x) { replace(x, is.na(x), 0) }
baseR.for <- function(x) { for(j in 1:ncol(x))
x[[j]][is.na(x[[j]])] = 0 }
# tidyverse
## dplyr
dplyr_if_else <- function(x) { mutate_all(x, ~if_else(is.na(.), 0, .)) }
dplyr_coalesce <- function(x) { mutate_all(x, ~coalesce(., 0)) }
## tidyr
tidyr_replace_na <- function(x) { replace_na(x, as.list(setNames(rep(0, 10), as.list(c(paste0("var", 1:10)))))) }
## hybrid
hybrd.ifelse <- function(x) { mutate_all(x, ~ifelse(is.na(.), 0, .)) }
hybrd.replace_na <- function(x) { mutate_all(x, ~replace_na(., 0)) }
hybrd.replace <- function(x) { mutate_all(x, ~replace(., is.na(.), 0)) }
hybrd.rplc_at.idx<- function(x) { mutate_at(x, c(1:10), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.nse<- function(x) { mutate_at(x, vars(var1:var10), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.stw<- function(x) { mutate_at(x, vars(starts_with("var")), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.ctn<- function(x) { mutate_at(x, vars(contains("var")), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.mtc<- function(x) { mutate_at(x, vars(matches("\\d+")), ~replace(., is.na(.), 0)) }
hybrd.rplc_if <- function(x) { mutate_if(x, is.numeric, ~replace(., is.na(.), 0)) }
# data.table
library(data.table)
DT.for.set.nms <- function(x) { for (j in names(x))
set(x,which(is.na(x[[j]])),j,0) }
DT.for.set.sqln <- function(x) { for (j in seq_len(ncol(x)))
set(x,which(is.na(x[[j]])),j,0) }
DT.nafill <- function(x) { nafill(df, fill=0)}
DT.setnafill <- function(x) { setnafill(df, fill=0)}
library(microbenchmark)
# 20% NA filled dataframe of 10 Million rows and 10 columns
set.seed(42) # to recreate the exact dataframe
dfN <- as.data.frame(matrix(sample(c(NA, as.numeric(1:4)), 1e7*10, replace = TRUE),
dimnames = list(NULL, paste0("var", 1:10)),
ncol = 10))
# Running 600 trials with each replacement method
# (the functions are excecuted locally - so that the original dataframe remains unmodified in all cases)
perf_results <- microbenchmark(
hybrid.ifelse = hybrid.ifelse(copy(dfN)),
dplyr_if_else = dplyr_if_else(copy(dfN)),
hybrd.replace_na = hybrd.replace_na(copy(dfN)),
baseR.sbst.rssgn = baseR.sbst.rssgn(copy(dfN)),
baseR.replace = baseR.replace(copy(dfN)),
dplyr_coalesce = dplyr_coalesce(copy(dfN)),
tidyr_replace_na = tidyr_replace_na(copy(dfN)),
hybrd.replace = hybrd.replace(copy(dfN)),
hybrd.rplc_at.ctn= hybrd.rplc_at.ctn(copy(dfN)),
hybrd.rplc_at.nse= hybrd.rplc_at.nse(copy(dfN)),
baseR.for = baseR.for(copy(dfN)),
hybrd.rplc_at.idx= hybrd.rplc_at.idx(copy(dfN)),
DT.for.set.nms = DT.for.set.nms(copy(dfN)),
DT.for.set.sqln = DT.for.set.sqln(copy(dfN)),
times = 600L
)
> print(perf_results) Unit: milliseconds expr min lq mean median uq max neval hybrd.ifelse 6171.0439 6339.7046 6425.221 6407.397 6496.992 7052.851 600 dplyr_if_else 3737.4954 3877.0983 3953.857 3946.024 4023.301 4539.428 600 hybrd.replace_na 1497.8653 1706.1119 1748.464 1745.282 1789.804 2127.166 600 baseR.sbst.rssgn 1480.5098 1686.1581 1730.006 1728.477 1772.951 2010.215 600 baseR.replace 1457.4016 1681.5583 1725.481 1722.069 1766.916 2089.627 600 dplyr_coalesce 1227.6150 1483.3520 1524.245 1519.454 1561.488 1996.859 600 tidyr_replace_na 1248.3292 1473.1707 1521.889 1520.108 1570.382 1995.768 600 hybrd.replace 913.1865 1197.3133 1233.336 1238.747 1276.141 1438.646 600 hybrd.rplc_at.ctn 916.9339 1192.9885 1224.733 1227.628 1268.644 1466.085 600 hybrd.rplc_at.nse 919.0270 1191.0541 1228.749 1228.635 1275.103 2882.040 600 baseR.for 869.3169 1180.8311 1216.958 1224.407 1264.737 1459.726 600 hybrd.rplc_at.idx 839.8915 1189.7465 1223.326 1228.329 1266.375 1565.794 600 DT.for.set.nms 761.6086 915.8166 1015.457 1001.772 1106.315 1363.044 600 DT.for.set.sqln 787.3535 918.8733 1017.812 1002.042 1122.474 1321.860 600
ggplot(perf_results, aes(x=expr, y=time/10^9)) +
geom_boxplot() +
xlab('Expression') +
ylab('Elapsed Time (Seconds)') +
scale_y_continuous(breaks = seq(0,7,1)) +
coord_flip()
qplot(y=time/10^9, data=perf_results, colour=expr) +
labs(y = "log10 Scaled Elapsed Time per Trial (secs)", x = "Trial Number") +
coord_cartesian(ylim = c(0.75, 7.5)) +
scale_y_log10(breaks=c(0.75, 0.875, 1, 1.25, 1.5, 1.75, seq(2, 7.5)))
Khi các bộ dữ liệu trở nên lớn hơn, lịch sử của Tidyrreplace_na
đã rút ra trước mặt. Với bộ sưu tập 100M điểm dữ liệu hiện tại để chạy qua, nó thực hiện gần như chính xác cũng như Base R For Loop. Tôi tò mò muốn xem điều gì xảy ra với các datafram có kích thước khác nhau.
Ví dụ bổ sung cho mutate
và summarize
_at
và _all
các biến thể chức năng có thể được tìm thấy ở đây: https://rdrr.io/cran/dplyr/man/summarise_all.html
Bên cạnh đó, tôi thấy các cuộc biểu tình hữu ích và các bộ sưu tập các ví dụ ở đây: https: //blog.exploratory. io / dplyr-0-5-is-awesome-heres-why-be095fd4eb8a
Với lời cảm ơn đặc biệt đến:
local()
và (với sự giúp đỡ của bệnh nhân Frank cũng vậy) vai trò của sự ép buộc thầm lặng trong việc đẩy nhanh nhiều cách tiếp cận này. coalesce()
chức năng mới hơn và cập nhật phân tích.data.table
chức năng đủ tốt để cuối cùng đưa chúng vào đội hình.is.numeric()
thực sự kiểm tra.(Tất nhiên, vui lòng liên hệ và cung cấp cho họ upvote nếu bạn thấy những cách tiếp cận này hữu ích.)
Lưu ý về việc sử dụng Numerics của tôi: Nếu bạn có bộ dữ liệu số nguyên thuần, tất cả các hàm của bạn sẽ chạy nhanh hơn. Vui lòng xem công việc của alexiz_laz để biết thêm thông tin. IRL, tôi không thể nhớ đã gặp phải một tập dữ liệu chứa hơn 10-15% số nguyên, vì vậy tôi đang chạy các thử nghiệm này trên các tệp dữ liệu số đầy đủ.
Phần cứng được sử dụng CPU 3,9 GHz với RAM 24 GB
df1[j][is.na(df1[j])] = 0
là sai, nên làdf1[[j]][is.na(df1[[j]])] = 0
forLp_Sbst
dường như không phải là cách mà bất cứ ai cũng nên xem xét tiếp cận nó so vớiforLp_smplfSbst
coalesce()
tùy chọn vào và chạy lại mọi lúc. Cảm ơn bạn đã nâng niu để cập nhật.
Đối với một vectơ duy nhất:
x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0
Đối với một data.frame, tạo một hàm ngoài, sau apply
đó đến các cột.
Vui lòng cung cấp một ví dụ tái tạo lần sau như chi tiết tại đây:
is.na
là hàm chung và có các phương thức cho các đối tượng của data.frame
lớp. Vì vậy, cái này cũng sẽ làm việc trên data.frame
s!
methods(is.na)
lần đầu tiên, tôi giống như ai vậy?!? . Tôi yêu khi những thứ như thế xảy ra! =)
ví dụ dplyr:
library(dplyr)
df1 <- df1 %>%
mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))
Lưu ý: công trình này cho mỗi cột được lựa chọn, nếu chúng ta cần phải làm điều này cho tất cả các cột, xem @reidjax câu trả lời 's sử dụng mutate_each .
Nếu chúng ta đang cố gắng thay thế NA
s khi xuất, ví dụ như khi viết vào csv, thì chúng ta có thể sử dụng:
write.csv(data, "data.csv", na = "0")
Tôi biết câu hỏi đã được trả lời, nhưng thực hiện theo cách này có thể hữu ích hơn với một số người:
Xác định chức năng này:
na.zero <- function (x) {
x[is.na(x)] <- 0
return(x)
}
Bây giờ bất cứ khi nào bạn cần chuyển đổi NA trong một vectơ thành 0, bạn có thể làm:
na.zero(some.vector)
Với dplyr
0.5.0, bạn có thể sử dụng coalesce
chức năng có thể dễ dàng tích hợp vào %>%
đường ống bằng cách thực hiện coalesce(vec, 0)
. Điều này thay thế tất cả các NA vec
bằng 0:
Giả sử chúng ta có khung dữ liệu với NA
s:
library(dplyr)
df <- data.frame(v = c(1, 2, 3, NA, 5, 6, 8))
df
# v
# 1 1
# 2 2
# 3 3
# 4 NA
# 5 5
# 6 6
# 7 8
df %>% mutate(v = coalesce(v, 0))
# v
# 1 1
# 2 2
# 3 3
# 4 0
# 5 5
# 6 6
# 7 8
Cách tiếp cận chung hơn về việc sử dụng replace()
trong ma trận hoặc vectơ để thay thế NA
cho0
Ví dụ:
> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1
Đây cũng là một thay thế cho việc sử dụng ifelse()
trongdplyr
df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
mutate(col = replace(col,is.na(col),0))
levels(A$x) <- append(levels(A$x), "notAnswered") A$x <- replace(A$x,which(is.na(A$x)),"notAnswered")
which
không cần thiết ở đây, bạn có thể sử dụng x1 <- replace(x,is.na(x),1)
.
NA
cho0
chỉ trong một cột cụ thể trong một khung dữ liệu lớn và chức năng này replace()
làm việc một cách hiệu quả nhất trong khi cũng chỉ đơn giản nhất.
Một ví dụ khác sử dụng gói imputeTS :
library(imputeTS)
na.replace(yourDataframe, 0)
Nếu bạn muốn thay thế NA trong các biến nhân tố, điều này có thể hữu ích:
n <- length(levels(data.vector))+1
data.vector <- as.numeric(data.vector)
data.vector[is.na(data.vector)] <- n
data.vector <- as.factor(data.vector)
levels(data.vector) <- c("level1","level2",...,"leveln", "NAlevel")
Nó biến đổi một vectơ nhân tố thành một vectơ số và thêm một mức yếu tố số nhân tạo khác, sau đó được chuyển trở lại thành một vectơ nhân tố với thêm một "cấp NA" mà bạn chọn.
Đã nhận xét về bài đăng của @ ianmunoz nhưng tôi không có đủ danh tiếng. Bạn có thể kết hợp dplyr
's mutate_each
và replace
để chăm sóc của NA
để 0
thay thế. Sử dụng khung dữ liệu từ câu trả lời của @ aL3xa ...
> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
> d
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1 4 8 1 9 6 9 NA 8 9 8
2 8 3 6 8 2 1 NA NA 6 3
3 6 6 3 NA 2 NA NA 5 7 7
4 10 6 1 1 7 9 1 10 3 10
5 10 6 7 10 10 3 2 5 4 6
6 2 4 1 5 7 NA NA 8 4 4
7 7 2 3 1 4 10 NA 8 7 7
8 9 5 8 10 5 3 5 8 3 2
9 9 1 8 7 6 5 NA NA 6 7
10 6 10 8 7 1 1 2 2 5 7
> d %>% mutate_each( funs_( interp( ~replace(., is.na(.),0) ) ) )
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1 4 8 1 9 6 9 0 8 9 8
2 8 3 6 8 2 1 0 0 6 3
3 6 6 3 0 2 0 0 5 7 7
4 10 6 1 1 7 9 1 10 3 10
5 10 6 7 10 10 3 2 5 4 6
6 2 4 1 5 7 0 0 8 4 4
7 7 2 3 1 4 10 0 8 7 7
8 9 5 8 10 5 3 5 8 3 2
9 9 1 8 7 6 5 0 0 6 7
10 6 10 8 7 1 1 2 2 5 7
Chúng tôi đang sử dụng đánh giá tiêu chuẩn (SE) ở đây, đó là lý do tại sao chúng tôi cần gạch dưới " funs_
." Chúng tôi cũng sử dụng lazyeval
's interp
/ ~
và các .
tham chiếu "mọi thứ chúng tôi đang làm việc", tức là khung dữ liệu. Bây giờ có số không!
Bạn có thể dùng replace()
Ví dụ:
> x <- c(-1,0,1,0,NA,0,1,1)
> x1 <- replace(x,5,1)
> x1
[1] -1 0 1 0 1 0 1 1
> x1 <- replace(x,5,mean(x,na.rm=T))
> x1
[1] -1.00 0.00 1.00 0.00 0.29 0.00 1.00 1.00
NA
s trong vector của bạn. Nó tốt cho các vectơ nhỏ như trong ví dụ của bạn.
x1 <- replace(x,is.na(x),1)
sẽ hoạt động mà không liệt kê rõ ràng các giá trị chỉ mục.
Một dplyr
tùy chọn tương thích ống khác với tidyr
phương thức replace_na
hoạt động cho một số cột:
require(dplyr)
require(tidyr)
m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
d <- as.data.frame(m)
myList <- setNames(lapply(vector("list", ncol(d)), function(x) x <- 0), names(d))
df <- d %>% replace_na(myList)
Bạn có thể dễ dàng hạn chế ví dụ như cột số:
d$str <- c("string", NA)
myList <- myList[sapply(d, is.numeric)]
df <- d %>% replace_na(myList)
Chức năng chuyên dụng ( nafill
/ setnafill
) cho mục đích đó là trong data.table
phiên bản gần đây
install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
library(data.table)
ans_df = nafill(df, fill=0)
setnafill(df, fill=0) # this one updates in-place
Để thay thế tất cả các NA trong khung dữ liệu, bạn có thể sử dụng:
df %>% replace(is.na(.), 0)
nếu bạn muốn gán tên mới sau khi thay đổi NA trong một cột cụ thể trong trường hợp này là cột V3, hãy sử dụng bạn cũng có thể làm như thế này
my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)