データフレームに列を名前でドロップする方法


304

大きなデータセットを持っているので、特定の列を読み取るか、他のすべての列を削除したいと考えています。

data <- read.dta("file.dta")

興味のない列を選択します。

var.out <- names(data)[!names(data) %in% c("iden", "name", "x_serv", "m_serv")]

そして私がしたいことよりも:

for(i in 1:length(var.out)) {
   paste("data$", var.out[i], sep="") <- NULL
}

不要な列をすべて削除します。これは最適なソリューションですか?


1
問題の上で寝て、私はsubset(data, select=c(...))変数を落とすために私の場合に役立つと思っていました。しかし問題は主paste("data$",var.out[i],sep="")にループ内の対象の列にアクセスする部分に関するものでした。列名を貼り付ける、またはなんとかして構成するにはどうすればよいですか?皆様のご協力とご協力に感謝します
leroux

回答:


380

インデックスまたはsubset関数を使用する必要があります。例えば ​​:

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8

次に、which関数と-演算子を列インデックスで使用できます。

R> df[ , -which(names(df) %in% c("z","u"))]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

または、もっと簡単selectに、subset関数の引数を使用します。-列名のベクトルで演算子を直接使用でき、名前の前後の引用符も省略できます。

R> subset(df, select=-c(z,u))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

他の列をドロップする代わりに、必要な列を選択することもできます。

R> df[ , c("x","y")]
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

R> subset(df, select=c(x,y))
  x y
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6

2
関数のselect引数はsubset完全に機能しました!ジュバありがとう!
leroux

2
whichは必要ありません。Istaの回答を参照してください。しかし、サブセット-はいいです!知らなかった!
TMS 2013

5
subset見た目は良いですが、欠落している値を静かに削除する方法は、私にとってかなり危険に思えます。
static_rtti 14年

2
subset確かに非常に便利ですが、Rをインタラクティブに使用しているのでない限り、使用しないでください。詳細については、関数のドキュメントの警告このSOの質問を参照してください。
Waldir Leoncio 2014

4
「名前の前後の引用符を省略することもできます!」実際には引用符を省略する必要があります。そうしないと、単項演算子に無効な引数が渡されます。名前に特定の文字( "-"など)が含まれている場合、引用符を削除するとRがコードを適切に解析できなくなるため、この方法はまったく使用できません。
oh54 '19 / 07/19

122

-which()これには使用しないでください。非常に危険です。検討してください:

dat <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
dat[ , -which(names(dat) %in% c("z","u"))] ## works as expected
dat[ , -which(names(dat) %in% c("foo","bar"))] ## deletes all columns! Probably not what you wanted...

代わりに、サブセットまたは!関数を使用します。

dat[ , !names(dat) %in% c("z","u")] ## works as expected
dat[ , !names(dat) %in% c("foo","bar")] ## returns the un-altered data.frame. Probably what you want

これは辛い経験から学んだ。使いすぎないでくださいwhich()


31
setdiffも便利です:setdiff(names(dat), c("foo", "bar"))
ハドリー、2011年

setdiff@hadleyの提案は、名前の長いリストのための非常に良いです。
JASC

48

まず、同じデータフレームで作業している場合は、列名に再度アクセスする代わりに、(ブールベクトルを使用した)直接インデックス付けを使用できます。Istaが指摘したように、より安全になり、書き込みと実行が速くなります。したがって、必要なのは次のとおりです。

var.out.bool <- !names(data) %in% c("iden", "name", "x_serv", "m_serv")

次に、単にデータを再割り当てします。

data <- data[,var.out.bool] # or...
data <- data[,var.out.bool, drop = FALSE] # You will need this option to avoid the conversion to an atomic vector if there is only one column left

2つ目は、書き込みが速く、削除する列にNULLを直接割り当てることができます。

data[c("iden", "name", "x_serv", "m_serv")] <- list(NULL) # You need list() to respect the target structure.

最後に、subset()を使用できますが、コードで実際に使用することはできません(ヘルプファイルでさえ警告されています)。具体的には、私にとっての問題は、susbset()のドロップ機能を直接使用する場合、列名に対応する式を引用符なしで記述する必要があることです。

subset( data, select = -c("iden", "name", "x_serv", "m_serv") ) # WILL NOT WORK
subset( data, select = -c(iden, name, x_serv, m_serv) ) # WILL

おまけとして、ここにさまざまなオプションの小さなベンチマークがあります。これは、サブセットが遅いこと、および最初の再割り当て方法が速いことを明確に示しています。

                                        re_assign(dtest, drop_vec)  46.719  52.5655  54.6460  59.0400  1347.331
                                      null_assign(dtest, drop_vec)  74.593  83.0585  86.2025  94.0035  1476.150
               subset(dtest, select = !names(dtest) %in% drop_vec) 106.280 115.4810 120.3435 131.4665 65133.780
 subset(dtest, select = names(dtest)[!names(dtest) %in% drop_vec]) 108.611 119.4830 124.0865 135.4270  1599.577
                                  subset(dtest, select = -c(x, y)) 102.026 111.2680 115.7035 126.2320  1484.174

マイクロベンチグラフ

コードは以下のとおりです。

dtest <- data.frame(x=1:5, y=2:6, z = 3:7)
drop_vec <- c("x", "y")

null_assign <- function(df, names) {
  df[names] <- list(NULL)
  df
}

re_assign <- function(df, drop) {
  df <- df [, ! names(df) %in% drop, drop = FALSE]
  df
}

res <- microbenchmark(
  re_assign(dtest,drop_vec),
  null_assign(dtest,drop_vec),
  subset(dtest, select = ! names(dtest) %in% drop_vec),
  subset(dtest, select = names(dtest)[! names(dtest) %in% drop_vec]),
  subset(dtest, select = -c(x, y) ),
times=5000)

plt <- ggplot2::qplot(y=time, data=res[res$time < 1000000,], colour=expr)
plt <- plt + ggplot2::scale_y_log10() + 
  ggplot2::labs(colour = "expression") + 
  ggplot2::scale_color_discrete(labels = c("re_assign", "null_assign", "subset_bool", "subset_names", "subset_drop")) +
  ggplot2::theme_bw(base_size=16)
print(plt)

2
私はを使用した2番目の代替案が好きですNULLが、それを割り当てるために3つ以上の名前を付ける必要があるのはなぜlist(NULL)ですか?私は1人の名前だけで試してみましたので、私は、それがどのように動作するかを知ってのみ好奇心だと私は必要ありませんlist()
ダーウィンPC

3
@DarwinPCはい。($または[[を使用して)1つのベクトル要素に直接アクセスする場合、<- list(NULL)実際に使用すると誤った結果になります。1つまたは複数の列でデータフレームのサブセットにアクセスする場合<- list(NULL)、1列のデータフレームで必要ない場合でも、これが適切な方法です(df['myColumns']必要に応じてベクターにキャストされるため)。
AntoineLizée15年

27

dplyrパッケージを試すこともできます:

R> df <- data.frame(x=1:5, y=2:6, z=3:7, u=4:8)
R> df
  x y z u
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
4 4 5 6 7
5 5 6 7 8
R> library(dplyr)
R> dplyr::select(df2, -c(x, y))  # remove columns x and y
  z u
1 3 4
2 4 5
3 5 6
4 6 7
5 7 8

4
dplyr::select(df2, -one_of(c('x','y')))名前付き列の一部が存在しない場合でも、使用は(警告付きで)機能します
divibisan

13

これに対する簡単な解決策を次に示します。たとえば、A、B、Cの3つの列を持つデータフレームXがあるとします。

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6))
> X
  A B C
1 1 3 5
2 2 4 6

列を削除する場合、たとえばBとすると、colnamesでgrepを使用して列インデックスを取得します。これを使用して列を省略できます。

> X<-X[,-grep("B",colnames(X))]

新しいXデータフレームは次のようになります(今回はB列がない)。

> X
  A C
1 1 5
2 2 6

grepの優れた点は、正規表現に一致する複数の列を指定できることです。5つの列(A、B、C、D、E)を持つXがある場合:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10

列BとDを取り出します。

> X<-X[,-grep("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

編集:以下のコメントでマシュー・ランドバーグの大げさな提案を検討してください:

> X<-data.frame(A=c(1,2),B=c(3,4),C=c(5,6),D=c(7,8),E=c(9,10))
> X
  A B C D  E
1 1 3 5 7  9
2 2 4 6 8 10
> X<-X[,!grepl("B|D",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

存在しない列を削除しようとしても、何も起こりません。

> X<-X[,!grepl("G",colnames(X))]
> X
  A C  E
1 1 5  9
2 2 6 10

3
X[,-grep("B",colnames(X))]列名にが含まれていない場合、B必要に応じてすべての列を返すのではなく、列を返しません。X <- iris例について考えてみましょう。これは、計算された値で負のインデックスを使用する場合の問題です。grepl代わりに検討してください。
Matthew Lundberg、2015

6

パッケージの使用中に列を削除しようとするdata.tableと、予期しない結果が発生しました。以下は投稿する価値があると思う。ほんの少しの注意書き。

[マシュー編集...]

DF = read.table(text = "
     fruit state grade y1980 y1990 y2000
     apples Ohio   aa    500   100   55
     apples Ohio   bb      0     0   44
     apples Ohio   cc    700     0   33
     apples Ohio   dd    300    50   66
", sep = "", header = TRUE, stringsAsFactors = FALSE)

DF[ , !names(DF) %in% c("grade")]   # all columns other than 'grade'
   fruit state y1980 y1990 y2000
1 apples  Ohio   500   100    55
2 apples  Ohio     0     0    44
3 apples  Ohio   700     0    33
4 apples  Ohio   300    50    66

library('data.table')
DT = as.data.table(DF)

DT[ , !names(dat4) %in% c("grade")]    # not expected !! not the same as DF !!
[1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE

DT[ , !names(DT) %in% c("grade"), with=FALSE]    # that's better
    fruit state y1980 y1990 y2000
1: apples  Ohio   500   100    55
2: apples  Ohio     0     0    44
3: apples  Ohio   700     0    33
4: apples  Ohio   300    50    66

基本的に、の構文はdata.tableとまったく同じではありませんdata.frame。実際には多くの違いがあります。FAQ1.1およびFAQ 2.17を参照してください。警告されました!


1
または、を使用DT[,var.out := NULL]して、削除する列を削除することもできます。
mnel

サブセット(x、select = ...)メソッドはdata.framedata.tableクラスの両方で機能します
momeara

3

私はコードを次のように変更しました:

# read data
dat<-read.dta("file.dta")

# vars to delete
var.in<-c("iden", "name", "x_serv", "m_serv")

# what I'm keeping
var.out<-setdiff(names(dat),var.in)

# keep only the ones I want       
dat <- dat[var.out]

とにかく、ジュバの答えは私の問題の最善の解決策です!


なぜこれをループで行いたいのですか?ジュバの答えは、1つのステップでそれを行う方法を示しています。なぜもっと複雑にするのですか?
Ista

もちろん、コードで関数のselect引数を使用しsubsetます。単に列をドロップする以外の何かをしたい場合に備えて、ループ内の任意の列にアクセスする方法を確認したかっただけです。元のデータセットには約1200の変数があり、それらが正確にどこにあるかを知らずに、そのうち4つだけを使用することに興味があります。
leroux

2

ここに他の人に役立つかもしれない別の解決策があります。以下のコードは、大きなデータセットから少数の行と列を選択します。貼り付け機能を使用して、順番に番号が付けられた名前の列のセットを選択することを除いて、列はjubaの回答の1つと同様に選択されます。

df = read.table(text = "

state county city  region  mmatrix  X1 X2 X3    A1     A2     A3      B1     B2     B3      C1      C2      C3

  1      1     1      1     111010   1  0  0     2     20    200       4      8     12      NA      NA      NA
  1      2     1      1     111010   1  0  0     4     NA    400       5      9     NA      NA      NA      NA
  1      1     2      1     111010   1  0  0     6     60     NA      NA     10     14      NA      NA      NA
  1      2     2      1     111010   1  0  0    NA     80    800       7     11     15      NA      NA      NA

  1      1     3      2     111010   0  1  0     1      2      1       2      2      2      10      20      30
  1      2     3      2     111010   0  1  0     2     NA      1       2      2     NA      40      50      NA
  1      1     4      2     111010   0  1  0     1      1     NA      NA      2      2      70      80      90
  1      2     4      2     111010   0  1  0    NA      2      1       2      2     10     100     110     120

  1      1     1      3     010010   0  0  1    10     20     10     200    200    200       1       2       3
  1      2     1      3     001000   0  0  1    20     NA     10     200    200    200       4       5       9
  1      1     2      3     101000   0  0  1    10     10     NA     200    200    200       7       8      NA
  1      2     2      3     011010   0  0  1    NA     20     10     200    200    200      10      11      12

", sep = "", header = TRUE, stringsAsFactors = FALSE)
df

df2 <- df[df$region == 2, names(df) %in% c(paste("C", seq_along(1:3), sep=''))]
df2

#    C1  C2  C3
# 5  10  20  30
# 6  40  50  NA
# 7  70  80  90
# 8 100 110 120


-1

評判スコアが低いため、コメントで質問に回答できません。

貼り付け関数が文字列を返すため、次のコードではエラーが発生します

for(i in 1:length(var.out)) {
   paste("data$", var.out[i], sep="") <- NULL
}

これが可能な解決策です:

for(i in 1:length(var.out)) {

  text_to_source <- paste0 ("data$", var.out[i], "<- NULL") # Write a line of your
                                                  # code like a character string
  eval (parse (text=text_to_source)) # Source a text that contains a code
}

または単に:

for(i in 1:length(var.out)) {
  data[var.out[i]] <- NULL
}

-1
df = mtcars 
vsとamを削除します。これらは断定的だからです。データセットのvsは列番号8にあり、amは列番号9にあります

dfnum = df[,-c(8,9)]

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.