Ik heb een dataframe en sommige kolommen hebben NA
waarden.
Hoe vervang ik deze NA
waarden door nullen?
Antwoord 1, autoriteit 100%
Zie mijn opmerking in @gsk3 antwoord. Een eenvoudig voorbeeld:
> 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
Het is niet nodig om apply
toe te passen. =)
BEWERKEN
Kijk ook eens naar het norm
pakket. Het heeft veel leuke functies voor het analyseren van ontbrekende gegevens. =)
Antwoord 2, autoriteit 37%
De gehybridiseerde dplyr-opties zijn nu ongeveer 30% sneller dan de Base R-subset die opnieuw wordt toegewezen. Op een dataframe van 100M loopt mutate_all(~replace(., is.na(.), 0))
een halve seconde sneller dan de basis R d[is.na(d)] <- 0
optie. Wat men specifiek wil vermijden is het gebruik van een ifelse()
of een if_else()
. (De volledige 600 proefanalyse duurde meer dan 4,5 uur, voornamelijk vanwege het opnemen van deze benaderingen.) Zie de benchmarkanalyses hieronder voor de volledige resultaten.
Als je worstelt met enorme dataframes, is data.table
de snelste optie van allemaal: 40% sneller dan de standaard Base R-aanpak. Het wijzigt ook de aanwezige gegevens, waardoor u effectief met bijna twee keer zoveel gegevens tegelijk kunt werken.
Een clustering van andere handige vervangingsbenaderingen voor opgeruimde vakken
Locatie:
- index
mutate_at(c(5:10), ~replace(., is.na(.), 0))
- directe referentie
mutate_at(vars(var5:var10), ~replace(., is.na(.), 0))
- vaste overeenkomst
mutate_at(vars(contains("1")), ~replace(., is.na(.), 0))
- of in plaats van
contains()
, probeerends_with()
,starts_with()
- of in plaats van
- patroonovereenkomst
mutate_at(vars(matches("\\d{2}")), ~replace(., is.na(.), 0))
Voorwaardelijk:
(wijzig slechts één type en laat andere typen met rust.)
- gehele getallen
mutate_if(is.integer, ~replace(., is.na(.), 0))
- cijfers
mutate_if(is.numeric, ~replace(., is.na(.), 0))
- strings
mutate_if(is.character, ~replace(., is.na(.), 0))
De complete analyse –
Bijgewerkt voor dplyr 0.8.0: functies gebruiken het purrr-formaat ~
symbolen: vervangen verouderde funs()
-argumenten.
Geteste benaderingen:
# 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)}
De code voor deze analyse:
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
)
Samenvatting van resultaten
> 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
Boxplot van resultaten
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()
Kleurgecodeerde Scatterplot of Trials (met y-as op een logschaal)
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)))
Een opmerking over de andere high-performers
Toen de datasets groter worden, was Tidyr”s replace_na
historisch gezien de voorhoede. Met de huidige verzameling van 100 miljoen datapunten om doorheen te lopen, presteert het bijna net zo goed als een Base R For Loop. Ik ben benieuwd wat er gebeurt voor dataframes van verschillende grootte.
Aanvullende voorbeelden voor de functievarianten mutate
en summarize
_at
en _all
vindt u hier: https://rdrr.io/cran/dplyr/man/summarise_all.html
Daarnaast vond ik hier nuttige demonstraties en verzamelingen van voorbeelden: https ://blog.exploratory.io/dplyr-0-5-is-awesome-heres-why-be095fd4eb8a
Toeschrijvingen en waarderingen
Met speciale dank aan:
- Tyler Rinker en Akrun voor het demonstreren van microbenchmark.
- alexis_laz voor het helpen begrijpen van het gebruik van
local()
, en ( ook met Franks geduldige hulp) de rol die stille dwang speelt bij het versnellen van veel van deze benaderingen. - ArthurYip voor de poke om de nieuwere functie
coalesce()
toe te voegen en de analyse bij te werken. - Gregor voor het duwtje om erachter te komen dat de
data.table
-functies goed genoeg zijn om ze uiteindelijk in de opstelling op te nemen. - Base R For-loop: alexis_laz
- data.table For Loops: Matt_Dowle
- Roman voor het uitleggen wat
is.numeric()
echt test.
(Neem natuurlijk contact met hen op en geef ze ook upvotes als u deze benaderingen nuttig vindt.)
Opmerking over mijn gebruik van numeriek: Als je een pure integer-dataset hebt, zullen al je functies sneller werken. Bekijk alexiz_laz’s werk voor meer informatie. IRL, ik kan me niet herinneren dat ik een dataset ben tegengekomen met meer dan 10-15% gehele getallen, dus ik voer deze tests uit op volledig numerieke dataframes.
Gebruikte hardware
3,9 GHz CPU met 24 GB RAM
Antwoord 3, autoriteit 14%
Voor een enkele vector:
x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0
Maak voor een data.frame een functie van het bovenstaande en apply
op de kolommen.
Geef de volgende keer een reproduceerbaar voorbeeld, zoals hier beschreven:
Hoe maak je een geweldig R-reproduceerbaar voorbeeld?
Antwoord 4, autoriteit 8%
dplyr voorbeeld:
library(dplyr)
df1 <- df1 %>%
mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))
Opmerking: dit werkt per geselecteerde kolom, als we dit voor alle kolommen moeten doen, zie het antwoord van @reidjax met muteer_elk.
Antwoord 5, autoriteit 6%
Als we NA
s proberen te vervangen bij het exporteren, bijvoorbeeld bij het schrijven naar csv, dan kunnen we het volgende gebruiken:
write.csv(data, "data.csv", na = "0")
Antwoord 6, autoriteit 5%
Ik weet dat de vraag al beantwoord is, maar het op deze manier doen kan voor sommigen nuttiger zijn:
Definieer deze functie:
na.zero <- function (x) {
x[is.na(x)] <- 0
return(x)
}
Als je nu NA’s in een vector naar nullen moet converteren, kun je het volgende doen:
na.zero(some.vector)
Antwoord 7, autoriteit 4%
Het is ook mogelijk om tidyr::replace_na
te gebruiken.
library(tidyr)
df <- df %>% mutate_all(funs(replace_na(.,0)))
Bewerken (dplyr > 1.0.0):
df %>% mutate(across(everything(), .fns = ~replace_na(.,0)))
Antwoord 8, autoriteit 3%
Meer algemene benadering van het gebruik van replace()
in matrix of vector om NA
te vervangen door 0
Bijvoorbeeld:
> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1
Dit is ook een alternatief voor het gebruik van ifelse()
in dplyr
df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
mutate(col = replace(col,is.na(col),0))
Antwoord 9, autoriteit 2%
Met dplyr
0.5.0 kunt u de functie coalesce
gebruiken die eenvoudig kan worden geïntegreerd in de %>%
-pijplijn door coalesce(vec, 0)
. Dit vervangt alle NA’s in vec
door 0:
Stel dat we een dataframe hebben met 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
Antwoord 10
Zou gereageerd hebben op de post van @ianmunoz, maar ik heb niet genoeg reputatie. Je kunt dplyr
‘s mutate_each
en replace
combineren om de NA
tot 0
vervanging. Het dataframe gebruiken uit het antwoord van @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
We gebruiken hier standaardevaluatie (SE) en daarom hebben we het onderstrepingsteken op “funs_
” nodig. We gebruiken ook lazyeval
‘s interp
/~
en de .
verwijst naar “alles waar we mee werken”, dwz het dataframe. Nu zijn er nullen!
Antwoord 11
Nog een voorbeeld waarbij het pakket imputeTS wordt gebruikt:
library(imputeTS)
na.replace(yourDataframe, 0)
Antwoord 12
Als u NA’s in factorvariabelen wilt vervangen, kan dit handig zijn:
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")
Het transformeert een factor-vector in een numerieke vector en voegt een ander kunstmatig numeriek factorniveau toe, dat vervolgens weer wordt getransformeerd naar een factor-vector met één extra “NA-niveau” naar keuze.
Antwoord 13
Om alle NA’s in een dataframe te vervangen, kunt u het volgende gebruiken:
df %>% replace(is.na(.), 0)
Antwoord 14
U kunt replace()
. gebruiken
Bijvoorbeeld:
> 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
Antwoord 15
Speciale functies, nafill
en setnafill
, staan voor dat doel in data.table
.
Indien beschikbaar, distribueren ze kolommen die moeten worden berekend over meerdere threads.
library(data.table)
ans_df <- nafill(df, fill=0)
# or even faster, in-place
setnafill(df, fill=0)
Antwoord 16
Een andere dplyr
pipe-compatibele optie met tidyr
methode replace_na
die werkt voor meerdere kolommen:
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)
Je kunt je eenvoudig beperken tot b.v. numerieke kolommen:
d$str <- c("string", NA)
myList <- myList[sapply(d, is.numeric)]
df <- d %>% replace_na(myList)
Antwoord 17
Deze eenvoudige functie geëxtraheerd uit Datacamp kan helpen:
replace_missings <- function(x, replacement) {
is_miss <- is.na(x)
x[is_miss] <- replacement
message(sum(is_miss), " missings replaced by the value ", replacement)
x
}
Dan
replace_missings(df, replacement = 0)
Antwoord 18
Een gemakkelijke manier om het te schrijven is met if_na
van hablar
:
library(dplyr)
library(hablar)
df <- tibble(a = c(1, 2, 3, NA, 5, 6, 8))
df %>%
mutate(a = if_na(a, 0))
die terugkeert:
a
<dbl>
1 1
2 2
3 3
4 0
5 5
6 6
7 8
Antwoord 19
Het cleaner
-pakket heeft een algemene na_replace()
, die standaard numerieke waarden vervangt door nullen, logische waarden door FALSE
, datums met vandaag, enz.:
starwars %>% na_replace()
na_replace(starwars)
Het ondersteunt zelfs gevectoriseerde vervangingen:
mtcars[1:6, c("mpg", "hp")] <- NA
na_replace(mtcars, mpg, hp, replacement = c(999, 123))
Documentatie: https://msberends.github.io/cleaner/reference/na_replace .html
Antwoord 20
als u een nieuwe naam wilt toewijzen na het wijzigen van de NA’s in een specifieke kolom in dit geval kolom V3, kunt u dit ook als volgt doen
my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)
Antwoord 21
Ik wil een volgende oplossing toevoegen die een populaire Hmisc
pakket.
library(Hmisc)
data(airquality)
# imputing with 0 - all columns
# although my favorite one for simple imputations is Hmisc::impute(x, "random")
> dd <- data.frame(Map(function(x) Hmisc::impute(x, 0), airquality))
> str(dd[[1]])
'impute' Named num [1:153] 41 36 12 18 0 28 23 19 8 0 ...
- attr(*, "names")= chr [1:153] "1" "2" "3" "4" ...
- attr(*, "imputed")= int [1:37] 5 10 25 26 27 32 33 34 35 36 ...
> dd[[1]][1:10]
1 2 3 4 5 6 7 8 9 10
41 36 12 18 0* 28 23 19 8 0*
Je kon zien dat alle metadata van imputaties als attributen worden toegewezen. Zo kan het later worden gebruikt.
Antwoord 22
in data.frame is het niet nodig om een nieuwe kolom te maken door te muteren.
library(tidyverse)
k <- c(1,2,80,NA,NA,51)
j <- c(NA,NA,3,31,12,NA)
df <- data.frame(k,j)%>%
replace_na(list(j=0))#convert only column j, for example
resultaat
k j
1 0
2 0
80 3
NA 31
NA 12
51 0
Antwoord 23
dplyr >= 1.0.0
In nieuwere versies van dplyr
:
across() vervangt de familie van "scoped-varianten" zoals summarise_at(), summarise_if() en summarise_all().
df <- data.frame(a = c(LETTERS[1:3], NA), b = c(NA, 1:3))
library(tidyverse)
df %>%
mutate(across(where(anyNA), ~ replace_na(., 0)))
a b
1 A 0
2 B 1
3 C 2
4 0 3
Deze code dwingt 0
als teken in de eerste kolom. Om NA
te vervangen op basis van het kolomtype, kun je een purrr-achtige formule gebruiken in where
:
df %>%
mutate(across(where(~ anyNA(.) & is.character(.)), ~ replace_na(., "0")))
Antwoord 24
Dit is niet echt een nieuwe oplossing, maar ik schrijf graag inline lambda’s die dingen afhandelen die ik pakketten niet helemaal kan laten doen. In dit geval,
df %>%
(function(x) { x[is.na(x)] <- 0; return(x) })
Omdat R nooit "voorbij object" zoals je zou kunnen zien in Python, wijzigt deze oplossing de originele variabele df
niet, en zal dus hetzelfde doen als de meeste andere oplossingen, maar met veel minder behoefte aan ingewikkelde kennis van bepaalde pakketten.
Let op de haakjes rond de functiedefinitie! Hoewel het mij een beetje overbodig lijkt, is het, aangezien de functiedefinitie tussen accolades staat, vereist dat inline-functies tussen haakjes worden gedefinieerd voor magrittr
.