データフレームの列を名前で削除する


874

データフレームから削除したい列がいくつかあります。私はそれらを個別に削除できることを知っています:

df$x <- NULL

しかし、私はより少ないコマンドでこれを行うことを望んでいました。

また、次のような整数インデックスを使用して列を削除できることも知っています。

df <- df[ -c(1, 3:6, 12) ]

しかし、私は変数の相対的な位置が変化するのではないかと心配しています。

Rがどれほど強力であるかを考えると、各列を1つずつ削除するよりも良い方法があると考えました。


13
Rがのような単純なものを持っていない理由を誰かに説明してdf#drop(var_name)もらえますか?代わりに、これらの複雑な回避策を実行する必要がありますか?
ifly6

2
@ ifly6 Rの「subset()」関数は、Pythonの「drop()」関数とほぼ同じくらい簡単ですが、軸の引数を指定する必要はありません...列を削除するなどの基本的なことを行うために、全体に1つだけの、究極の簡単なキーワード/構文を実装します。
Paul Sochacki

回答:


912

名前の単純なリストを使用できます。

DF <- data.frame(
  x=1:10,
  y=10:1,
  z=rep(5,10),
  a=11:20
)
drops <- c("x","z")
DF[ , !(names(DF) %in% drops)]

または、代わりに、保持するリストを作成し、名前で参照することもできます。

keeps <- c("y", "a")
DF[keeps]

編集:dropインデックス関数の引数をまだ知らない人のために、1つの列をデータフレームとして保持したい場合は、次のようにします。

keeps <- "y"
DF[ , keeps, drop = FALSE]

drop=TRUE(または言及しない)は不要な次元を削除するため、columnの値を持つベクトルを返しますy


19
サブセット関数は、1列のデータフレームをベクトルに変換しないため、より適切に機能します
mut1na

3
@ mut1naは、インデックス関数の引数drop = FALSEを確認します。
Joris Meys 2013年

4
DF[,keeps]代わりにすべきではないDF[keeps]ですか?
lindelof 2014年

8
@lindelofいいえ。可能ですが、単一の列のみを選択する場合にRがデータフレームをベクトルに変換しないようにするには、drop = FALSEを追加する必要があります。データフレームはリストであることを忘れないでください。リストの選択(私が行ったような1次元)は完全に機能し、常にリストを返します。または、この場合のデータフレーム。これが、私がそれを使用する理由です。
Joris Meys 2014年

7
@AjayOhriはい、そうです。コンマなしでは、「リスト」の選択方法を使用します。つまり、単一の列を抽出した場合でも、データフレームが返されます。「マトリックス」の方法を使用する場合は、1つの列のみを選択すると、データフレームではなくベクトルが取得されることに注意してください。これを回避するには、drop = FALSEを追加する必要があります。私の回答、そしてあなたの真上のコメントで説明されているように
Joris Meys '07 / 07/15

453

subset必要な列がわかっている場合に役立つコマンドもあります。

df <- data.frame(a = 1:10, b = 2:11, c = 3:12)
df <- subset(df, select = c(a, c))

@hadleyによるコメントの後に更新:列a、c を削除するには、次のようにします。

df <- subset(df, select = -c(a, c))

3
R subset関数に「allbut = FALSE」のようなオプションがあり、TRUEに設定すると選択が「反転」する、つまりリスト内の列を除くすべての列が保持されることを本当に望みますselect
Prasad Chalasani、2011年

4
@prasad、以下の@jorisの回答を参照してください。サブセット基準のないサブセットは少しやり過ぎです。単純に試してみてください:df[c("a", "c")]
JDロング

@JD私はそれを知っていましたが、subset列名を引用符で囲む必要がないコマンドの構文上の便利さを気に入っています-名前の引用を避けるためだけに、余分な文字をいくつか入力してもかまいません:)
プラサドチャラサニ

11
subset他の関数内では使用しないでください。
アリB.フリードマン


196
within(df, rm(x))

おそらく最も簡単です、または複数の変数の場合:

within(df, rm(x, y))

または、data.tables を処理している場合(data.tableの名前で列をどのように削除しますか?):

dt[, x := NULL]   # Deletes column x by reference instantly.

dt[, !"x"]   # Selects all but x into a new data.table.

または複数の変数

dt[, c("x","y") := NULL]

dt[, !c("x", "y")]

26
within(df, rm(x))、これまでで最もクリーンソリューション。これが可能性であるとすると、他のすべての答えは桁違いに不必要に複雑に見えます。
Miles Erickson、2015年

2
で名前が付けられた列が重複している場合、within(df, rm(x))は機能しないことに注意してください。xdf
MichaelChirico

2
明確にするために@MichaelChiricoはどちらも削除しませんが、データの値を変更するようです。これが事実である場合、より大きな問題がありますが、ここに例があります:df <- data.frame(x = 1, y = 2); names(df) <- c("x", "x"); within(df, rm(x))戻り値data.frame(x = 2, x = 2)
Max Ghenis 2017年

1
@MilesErickson問題は、within()強力であるがNSEも使用する関数に依存していることです。ヘルプページの注記には、プログラミングには十分な注意を払う必要があることが明記されています。
Joris Meys、2018

@MilesErickson名前が重複しているデータフレームに遭遇する頻度はどれくらいですか?
HSchmale

115

次の%in%ように使用できます:

df[, !(colnames(df) %in% c("x","bar","foo"))]

1
私は何かを逃していますか、またはこれは事実上、ジョリスの答えの最初の部分と同じ解決策ですか? DF[ , !(names(DF) %in% drops)]
Daniel Fletcher、

9
@DanielFletcher:それは同じです。回答のタイムスタンプを見てください。私たちは同時に答えました... 5年前。:)
Joshua Ulrich

5
ナッティー。 identical(post_time_1, post_time_2) [1] TRUE = D
ダニエルフレッチャー

54

list(NULL)も機能します:

dat <- mtcars
colnames(dat)
# [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
# [11] "carb"
dat[,c("mpg","cyl","wt")] <- list(NULL)
colnames(dat)
# [1] "disp" "hp"   "drat" "qsec" "vs"   "am"   "gear" "carb"

1
鮮やかさ!これは、自然な方法でNULL割り当てを単一の列に拡張し、(一見)コピーを回避します(内部で何が起きているのかわからないので、メモリ使用量ではより効率的ではないかもしれません...構文的にはより効率的です。)
c-urchin '20年

6
list(NULL)は必要ありません。NULLで十分です。例:dat [、4] = NULL
CousinCocaine 2014

8
OPの質問は、複数の列を削除する方法でした。dat [、4:5] <-NULLは機能しません。これがlist(NULL)の出番です。1つ以上の列で機能します。
Vincent

これは、重複した列名を削除しようとした場合にも機能しません。
MichaelChirico

@MichaelChirico私にとってはうまくいきます。同じ名前の最初の列を削除する場合はラベルを付けるか、削除する各列にインデックスを付けます。それが機能しない例がある場合、私はそれを見てみたいと思います。おそらく新しい質問として投稿しますか?
Vincent

42

参照によって列を削除し、関連する内部コピーを回避するdata.frames場合は、data.tableパッケージと関数を:=

文字ベクトル名を:=演算子の左側にNULL、RHSとして渡すことができます。

library(data.table)

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)
DT <- data.table(df)
# or more simply  DT <- data.table(a=1:10, b=1:10, c=1:10, d=1:10) #

DT[, c('a','b') := NULL]

名前をへの呼び出しの外で文字ベクトルとして事前定義する場合[は、オブジェクトの名前をラップする(){}、LHSをのスコープ内の名前としてではなく、呼び出しスコープで評価するように強制しますDT

del <- c('a','b')
DT <- data.table(a=1:10, b=1:10, c=1:10, d=1:10)
DT[, (del) := NULL]
DT <-  <- data.table(a=1:10, b=1:10, c=1:10, d=1:10)
DT[, {del} := NULL]
# force or `c` would also work.   

また、使用することができるsetのオーバーヘッドを回避する、[.data.tableとものために働きますdata.frames

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)
DT <- data.table(df)

# drop `a` from df (no copying involved)

set(df, j = 'a', value = NULL)
# drop `b` from DT (no copying involved)
set(DT, j = 'b', value = NULL)

41

grep()が数値ベクトルを返すという事実に基づいて、潜在的により強力な戦略があります。私のデータセットの1つで行うように変数の長いリストがある場合、「。A」で終わる変数と「.B」で終わる変数があり、「。A」で終わる変数だけが必要です(どちらのパターンにも一致しないすべての変数を使用して、次のようにします。

dfrm2 <- dfrm[ , -grep("\\.B$", names(dfrm)) ]

目下のケースでは、Joris Meysの例を使用すると、それほどコンパクトではないかもしれませんが、次のようになります。

DF <- DF[, -grep( paste("^",drops,"$", sep="", collapse="|"), names(DF) )]

1
我々が定義する場合dropsとして、最初の場所でpaste0("^", drop_cols, "$")と:、これは(よりコンパクト読む)非常に良くなったsapplyDF[ , -sapply(drops, grep, names(DF))]
MichaelChirico

30

別のdplyr答え。変数に共通の命名構造がある場合は、を試してみてくださいstarts_with()。例えば

library(dplyr)
df <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm (5), 
                 var4 = rnorm(5), char1 = rnorm(5), char2 = rnorm(5))
df
#        var2      char1        var4       var3       char2       var1
#1 -0.4629512 -0.3595079 -0.04763169  0.6398194  0.70996579 0.75879754
#2  0.5489027  0.1572841 -1.65313658 -1.3228020 -1.42785427 0.31168919
#3 -0.1707694 -0.9036500  0.47583030 -0.6636173  0.02116066 0.03983268
df1 <- df %>% select(-starts_with("char"))
df1
#        var2        var4       var3       var1
#1 -0.4629512 -0.04763169  0.6398194 0.75879754
#2  0.5489027 -1.65313658 -1.3228020 0.31168919
#3 -0.1707694  0.47583030 -0.6636173 0.03983268

変数のシーケンスをデータフレームにドロップする場合は、を使用できます:。あなたがドロップしたい場合たとえばvar2var3、およびすべての間での変数は、あなただけが残っているはずですvar1

df2 <- df1 %>% select(-c(var2:var3) )  
df2
#        var1
#1 0.75879754
#2 0.31168919
#3 0.03983268

1
他のすべてに付属している機会について忘れないselect()ような、contains()あるいはmatches()また、正規表現を受け入れ、。
ha_pu

23

別の可能性:

df <- df[, setdiff(names(df), c("a", "c"))]

または

df <- df[, grep('^(a|c)$', names(df), invert=TRUE)]

2
setdiff特に非常に多数の列の場合には、の使用が最適であるため、これ以上賛成されないのは残念です。
ctbrown 2014年

これに関する別の角度:df <- df[ , -which(grepl('a|c', names(df)))]
Joe

23
DF <- data.frame(
  x=1:10,
  y=10:1,
  z=rep(5,10),
  a=11:20
)
DF

出力:

    x  y z  a
1   1 10 5 11
2   2  9 5 12
3   3  8 5 13
4   4  7 5 14
5   5  6 5 15
6   6  5 5 16
7   7  4 5 17
8   8  3 5 18
9   9  2 5 19
10 10  1 5 20

DF[c("a","x")] <- list(NULL)

出力:

        y z
    1  10 5
    2   9 5
    3   8 5
    4   7 5
    5   6 5
    6   5 5
    7   4 5
    8   3 5    
    9   2 5
    10  1 5

23

Dplyrソリューション

これがここで大きな注目を集めるのではないかと思いますが、削除する列のリストがあり、節でdplyr使用するチェーンでそれを実行したい場合は、次のようにします。one_of()select

簡単で再現可能な例を次に示します。

undesired <- c('mpg', 'cyl', 'hp')

mtcars <- mtcars %>%
  select(-one_of(undesired))

ドキュメントは、実行する?one_ofかここにあります:

http://genomicsclass.github.io/book/pages/dplyr_tutorial.html


22

興味深いことに、これはRの奇妙な複数の構文不整合の1つを示しています。たとえば、2列のデータフレームがあるとします。

df <- data.frame(x=1, y=2)

これはデータフレームを与えます

subset(df, select=-y)

しかし、これはベクトルを与えます

df[,-2]

これはすべてで説明されて?[いますが、必ずしも期待される動作ではありません。まあ、少なくとも私には...


18

これを回避するdplyr方法は次のとおりです。

#df[ -c(1,3:6, 12) ]  # original
df.cut <- df %>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)  # with dplyr::select()

注釈なしで読み、理解するのは直感的であり、データフレーム内の列の位置が変化してもロバストであるため、これが好きです。また-、要素の削除に使用するベクトル化されたイディオムに従います。


これに加えて、(1)ユーザーは元のdfを置き換えたい(2)magrittrは%<>% 、簡略化できる入力オブジェクトを置き換える演算子を持っているdf %<>% select(-col.to.drop.1, -col.to.drop.2, ..., -col.to.drop.6)
Marek

1
あなたが持つドロップする列の長いリストを持っている場合dplyr、それはそれらのグループに容易になり、唯一のマイナス置くかもしれません:df.cut <- df %>% select(-c(col.to.drop.1, col.to.drop.2, ..., col.to.drop.n))
iNyar

14

私はもっ​​と良いイディオムがあるはずだと考え続けていますが、名前で列を減算するために、私は次のことをする傾向があります:

df <- data.frame(a=1:10, b=1:10, c=1:10, d=1:10)

# return everything except a and c
df <- df[,-match(c("a","c"),names(df))]
df

4
マッチを無効にするのは良い考えではありませんdf[,-match(c("e","f"),names(df))]
ハドリー、2011年

。@ JDLong-列名で始まる列を削除する場合はどうなり-ますか?
Chetan Arvind Patil、

12

これを正確にdropNamed()実行する関数がBernd BischlのBBmiscパッケージで呼び出されています。

BBmisc::dropNamed(df, "x")

利点は、データフレーム引数の繰り返しを回避できるため、magrittrdplyrアプローチと同様に)パイプインに適しています。

df %>% BBmisc::dropNamed("x")

9

上記の@hadleyを使用したくない場合の別の解決策:「COLUMN_NAME」が削除する列の名前の場合:

df[,-which(names(df) == "COLUMN_NAME")]

1
(1)問題は、一度に複数の列を削除することです。(2)がない場合COLUMN_NAMEは機能しませんdf(自分で確認してください:)df<-data.frame(a=1,b=2)。(3)の方df[,names(df) != "COLUMN_NAME"]が単純であり、(2)の
影響を

この回答について、もう少し詳しく教えてもらえますか?
Akash Nayak

8

向こうがselect(-one_of(drop_col_names))先に答えを実証し、夫婦他がありますdplyr使用して列を削除するためのオプションselect()それは(カラム名にいくつかのさまざまなdplyrスターウォーズのサンプルデータを使用して)すべての特定の列名を定義する必要ありません。

library(dplyr)
starwars %>% 
  select(-(name:mass)) %>%        # the range of columns from 'name' to 'mass'
  select(-contains('color')) %>%  # any column name that contains 'color'
  select(-starts_with('bi')) %>%  # any column name that starts with 'bi'
  select(-ends_with('er')) %>%    # any column name that ends with 'er'
  select(-matches('^f.+s$')) %>%  # any column name matching the regex pattern
  select_if(~!is.list(.)) %>%     # not by column name but by data type
  head(2)

# A tibble: 2 x 2
homeworld species
  <chr>     <chr>  
1 Tatooine  Human  
2 Tatooine  Droid 

データフレームに存在する場合と存在しない場合がある列を削除する必要がある場合、列名が存在しない場合にselect_if()使用one_of()するとUnknown columns:警告がスローされないという、ちょっとした工夫があります。この例では、「bad_column」はデータフレームの列ではありません。

starwars %>% 
  select_if(!names(.) %in% c('height', 'mass', 'bad_column'))

4

提供するデータフレームとの文字列をカンマで区切られた名前削除するには:

remove_features <- function(df, features) {
  rem_vec <- unlist(strsplit(features, ', '))
  res <- df[,!(names(df) %in% rem_vec)]
  return(res)
}

使用法

remove_features(iris, "Sepal.Length, Petal.Width")

ここに画像の説明を入力してください


1

を使用して、削除する列のインデックスを見つけますwhich。これらのインデックスに負の符号(*-1)を付けます。次に、それらの値をサブセット化し、データフレームから削除します。これは一例です。

DF <- data.frame(one=c('a','b'), two=c('c', 'd'), three=c('e', 'f'), four=c('g', 'h'))
DF
#  one two three four
#1   a   d     f    i
#2   b   e     g    j

DF[which(names(DF) %in% c('two','three')) *-1]
#  one four
#1   a    g
#2   b    h

1

あなたが大きくdata.frame、メモリ使用量が少ない場合[ あるいはrmwithinするための列を削除するdata.frame、などのsubset手動のヒントの横する-より多くのメモリを使用して、(R 3.6.2)現在使用してsubset対話的に

getData <- function() {
  n <- 1e7
  set.seed(7)
  data.frame(a = runif(n), b = runif(n), c = runif(n), d = runif(n))
}

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- DF[setdiff(names(DF), c("a", "c"))] ##
#DF <- DF[!(names(DF) %in% c("a", "c"))] #Alternative
#DF <- DF[-match(c("a","c"),names(DF))]  #Alternative
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- subset(DF, select = -c(a, c)) ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#357 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF <- within(DF, rm(a, c)) ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used

DF <- getData()
tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
DF[c("a", "c")]  <- NULL ##
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#0.1 MB are used
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.