RデータフレームでNA値をゼロに置き換えるにはどうすればよいですか?


回答:


880

@ gsk3回答で私のコメントを参照してください。簡単な例:

> 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

申請する必要はありませんapply。=)

編集する

また、normパッケージも確認してください。欠損データ分析のための素晴らしい機能がたくさんあります。=)


2
あなたがそれを投稿する前に私は昨日すでにこのコードを試しましたが、うまくいきませんでした。これは質問を投稿したからです。しかし、私は知ってみて完璧に働いた。私は何か間違ったことをしていたと思います。
Renato Dinhani

12
@RenatoDinhaniConceição:すでに何かを試した場合は、質問するときにその情報を共有すると役立ちます。問題のある場所を絞り込むのに役立ちます。
アーロンがスタックオーバーフローを去った

2
d [is.na(d)] <-0は私には意味がありません。それは後ろ向きですか?Rはこのステートメントをどのように処理しますか?
user798719

13
@ user798719-「<-」はRの代入演算子であり、次のように読み取ることができます。右側で何かを実行してから、左側の場所/名前に割り当てます。この場合、実際には何も「実行」しておらず、単にゼロを作成しています。左側は、dオブジェクト(角括弧)内のdオブジェクトを見て、TRUEを返すすべての要素を見つけます(is.na(d)は各要素の論理を返します)。それらが見つかったら、それらを値0に置き換えます(「割り当てます」)。これにより、すべての非NAがそのまま残り、欠落しているものだけが置き換えられます。
Twitch_City、2015

3
そして...データフレームがあり、特定の数値ベクトルにのみ置換を適用したい場合(たとえば、NAの文字列を残します):df[19:28][is.na(df[19:28])] <- 0
jtdoud

299

dplyrハイブリッドオプションは、Base Rサブセットの再割り当てよりも約30%高速です。100Mデータポイントでmutate_all(~replace(., is.na(.), 0))は、データフレームはベースR d[is.na(d)] <- 0オプションよりも0.5秒速く実行されます。特にを避けたいのは、ifelse()またはを使用することif_else()です。(完全な結果については、以下のベンチマーク分析をご覧ください。)

大規模なデータフレームに苦労している場合data.tableは、すべての中で最速のオプションです。標準のBase Rアプローチより40%高速です。また、所定の場所にあるデータを変更して、一度に2倍近くのデータを効率的に処理できるようにします。


他の役立つtidyverse置換アプローチのクラスタリング

場所的に:

  • インデックス 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()、試してends_with()starts_with()
  • パターンマッチ mutate_at(vars(matches("\\d{2}")), ~replace(., is.na(.), 0))

条件付き:(
単一のタイプのみを変更し、他のタイプはそのままにします。)

  • 整数 mutate_if(is.integer, ~replace(., is.na(.), 0))
  • mutate_if(is.numeric, ~replace(., is.na(.), 0))
  • 文字列 mutate_if(is.character, ~replace(., is.na(.), 0))

完全な分析-

dplyr 0.8.0向けに更新:関数はpurrr形式の~記号を使用:非推奨のfuns()引数を置き換えます。

テストされたアプローチ:

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

経過時間の箱ひげ図の比較

色分けされた試行の散布図(対数スケールのy軸付き)

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

すべての試行時間の散布図

他のハイパフォーマーに関するメモ

データセットが大きく取得する場合、Tidyr 'のは、replace_na歴史的に前に引き出さていました。実行する100Mデータポイントの現在のコレクションにより、ベースR Forループとほぼ同じように実行されます。異なるサイズのデータ​​フレームで何が起こるか知りたいです。

mutateand summarize _atおよび_all関数のバリアントの追加の例は、https://rdrr.io/cran/dplyr/man/summarise_all.htmlで見つけることができます さらに、https://blog.exploratoryで、役立つデモと例のコレクションを見つけました。 io / dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

帰属と感謝

特別な感謝を込めて:

  • マイクロベンチマークを実証してくれたTyler RinkerAkrun
  • alexis_lazは、の使用を理解するのを手伝ってくれたことlocal()、および(フランクの忍耐強い助けもあり)、これらのアプローチの多くを高速化する際にサイレント強制が果たす役割。
  • ポークがArthurYipに新しいcoalesce()関数を追加して分析を更新します。
  • ナッジがdata.table最終的にそれらをラインナップに含めるのに十分な機能を理解するためのグレゴール。
  • ベースR Forループ:alexis_laz
  • データ表ループの場合:Matt_Dowle
  • is.numeric()実際にテストするものを説明するためのRoman 。

(もちろん、これらのアプローチが有用であると思われる場合は、連絡を取り、賛成票を投じてください。)

私の 数値の使用に関する注意:純粋な整数データセットがある場合、すべての関数がより高速に実行されます。alexiz_lazの作品をご覧くださいをしてください。IRL、10〜15%を超える整数を含むデータセットに遭遇したことを思い出せないため、これらのテストを完全に数値のデータフレームで実行しています。

ハードウェア使用 3.9 GHz CPU、24 GB RAM


2
@フランク-その矛盾を見つけていただきありがとうございます。参照はすべてクリーンアップされ、結果は1台のマシンで完全に再実行され、再ポストされました。
leerssej 2017年

わかりました。また、私df1[j][is.na(df1[j])] = 0は間違っていると思います、それは間違っているはずですdf1[[j]][is.na(df1[[j]])] = 0
フランク

ああ、私はあなたがそれを各ベンチマークで異なって2回書いたことを知っています。とにかく、forLp_Sbst誰もがそれに取り組むことを検討する必要がある方法とは思えないforLp_smplfSbst
フランク

1
@UweBlock-素晴らしい質問です。これにより、すべての関数がまったく同じデータフレームで機能するように、左代入操作をサブセット化することができました。私はその機能の周りにローカルを巻き込む必要があったので、科学の名において[1つの仕事、あなたは1つの仕事をしました!]私はそれをすべての周りに巻きました。詳細については、こちらを参照してください:stackoverflow.com/questions/41604711 / ...かなり長い前の答えを切り詰めていましたが、ディスカッションのその部分は追加しておくとよいでしょう。ありがとうございます!
leerssej 2017

1
@ArthurYip-私はcoalesce()オプションを追加し、常に再実行しました。ナッジを更新していただきありがとうございます。
leerssej 2017

128

単一のベクトルの場合:

x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0

data.frameの場合は、上記の関数を作成しapplyてから列に追加します。

ここで詳しく説明するように、次回は再現可能な例を提供してください。

優れたRの再現可能な例を作る方法は?


18
is.naジェネリック関数であり、data.frameクラスのオブジェクト用のメソッドがあります。これはdata.frames でも機能します!
aL3xa

3
methods(is.na)初めて走ったときは、ワァァァァッ!。そのようなことが起こると私は大好きです!=)
aL3xa

9
単一のベクトルではなくdfという名前のデータフレームがあり、X3という名前の単一の列で欠落している観測を置き換える場合を考えます。これを行うには、次の行を使用します。df $ X3 [is.na(df $ X3)] <-0
Mark Miller

8
my.dfという名前のデータフレームの列4〜6のNAを0にのみ置換したいとします。使用できます:my.df [、4:6] [is.na(my.df [、4:6])] <-0
Mark Miller

どうして「x」をis.na(x)に渡すのですか?Rのどのライブラリルーチンがベクトル化されているかを知る方法はありますか?
uh_big_mike_boi

73

dplyrの例:

library(dplyr)

df1 <- df1 %>%
    mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))

注:これは選択した列ごとに機能します。すべての列に対してこれを行う必要がある場合は、機能します。 mutate_eachを使用した@reidjaxの回答を。


57

NAたとえばcsvに書き込むときなど、エクスポート時にs を置き換えようとしている場合は、次のように使用できます。

  write.csv(data, "data.csv", na = "0")

47

私は質問がすでに答えられていることを知っていますが、それをこのようにすることはいくつかにとってより便利かもしれません:

この関数を定義します。

na.zero <- function (x) {
    x[is.na(x)] <- 0
    return(x)
}

これで、ベクトルのNAをゼロに変換する必要があるときはいつでも行うことができます:

na.zero(some.vector)

22

ではdplyr0.5.0、あなたが使用することができcoalesce、容易に統合することができる機能%>%行うことにより、パイプラインをcoalesce(vec, 0)。これにより、すべてのNAが置き換えられますvec 0にられます。

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

合体をテストしましたが、パフォーマンスは置換とほぼ同じです。合体コマンドは、これまでで最も簡単です。
Arthur Yip 2017

それを2列以上のティブルのすべての列に適用する方法を示すと便利です。
jangorecki

21

replace()置換NAする行列またはベクトルで使用するより一般的な方法0

例えば:

> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1

これはifelse()dplyr

df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
   mutate(col = replace(col,is.na(col),0))

1
私の列は要因だったので、交換用の値を追加する必要がありましたlevels(A$x) <- append(levels(A$x), "notAnswered") A$x <- replace(A$x,which(is.na(A$x)),"notAnswered")
Climbs_lika_Spyder

1
whichここでは必要ありませんx1 <- replace(x,is.na(x),1)。使用できます。
lmo

私は交換するために、このスレッドで提案されている多くの方法を試してみましたNA0大規模なデータフレームに一つだけの特定の列に、この機能はreplace()最も効果もしばらく最も単純に働きました。
ドク

19

使用することも可能tidyr::replace_naです。

    library(tidyr)
    df <- df %>% mutate_all(funs(replace_na(.,0)))

9

imputeTSパッケージを使用した別の例:

library(imputeTS)
na.replace(yourDataframe, 0)

9

因子変数のNAを置き換える場合は、次のようにすると便利です。

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

因子ベクトルを数値ベクトルに変換し、別の人工的な数値因子レベルを追加します。次に、選択した「NAレベル」を1つ追加して因子ベクトルに変換します。


8

@ianmunozの投稿にコメントしたはずですが、評判がよくありません。あなたは組み合わせることができdplyrさんmutate_eachreplaceの世話をするためNA0交換。@ 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

ここでは標準評価(SE)を使用しているため、「」の下線が必要funs_です。また、lazyevalinterp/ 」と「参照するすべてのもの」~.参照、つまりデータフレームを使用します。今、ゼロがあります!


4

使用できます replace()

例えば:

> 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

6
真ですが、NAベクトルのs のインデックスがわかっている場合にのみ実用的です。あなたの例のように小さなベクトルには問題ありません。
dardisco 2013

4
@dardisco x1 <- replace(x,is.na(x),1)は、インデックス値を明示的にリストしなくても機能します。
lmo

4

複数のカラムで機能dplyrするtidyrメソッドreplace_naを備えた別のパイプ互換オプション:

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)

たとえば数値列に簡単に制限できます。

d$str <- c("string", NA)

myList <- myList[sapply(d, is.numeric)]

df <- d %>% replace_na(myList)

4

そのための専用機能(nafill/ setnafill)は最近のdata.tableバージョンにあります

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

反対票を投じている人のために、フィードバックも提供してください。そうすれば、私の答えを改善できます。
jangorecki

3

Datacampから抽出されたこの単純な関数は、次のことに役立ちます。

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
}

その後

replace_missings(df, replacement = 0)

3

それを書く簡単な方法はif_nafrom hablar

library(dplyr)
library(hablar)

df <- tibble(a = c(1, 2, 3, NA, 5, 6, 8))

df %>% 
  mutate(a = if_na(a, 0))

これは次を返します:

      a
  <dbl>
1     1
2     2
3     3
4     0
5     5
6     6
7     8

2

データフレーム内のすべてのNAを置き換えるには、以下を使用できます。

df %>% replace(is.na(.), 0)


これは新しいソリューションではありません
jogo

1

特定の列(この場合は列V3)のNAを変更した後で新しい名前を割り当てる場合は、次のように使用することもできます。

my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.