Hoe vervang ik NA-waarden door nullen in een R-dataframe?

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(), probeer ends_with(),starts_with()
  • 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()

Boxplot vergelijking van verstreken tijd

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

Scatterplot van alle proeftijden

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 NAs 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 NAs:

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 tidyrmethode 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.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

3 × 4 =

Other episodes