변수 이름이 문자형 벡터에 저장 될 때 data.table 선택 / 할당


91

data.table변수 이름이 문자형 벡터에 저장된 경우에서 변수 를 어떻게 참조 합니까? 예를 들어, 이것은 다음에서 작동합니다 data.frame.

df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
#   col1
# 1    4
# 2    5
# 3    6

:=표기법을 사용하거나 사용하지 않고 data.table에 대해 동일한 작업을 어떻게 수행 할 수 있습니까? 의 명백한 것은 dt[ , list(colname)]작동하지 않습니다 (또는 기대하지 않았습니다).

답변:


132

프로그래밍 방식으로 변수를 선택하는 두 가지 방법 :

  1. with = FALSE:

    DT = data.table(col1 = 1:3)
    colname = "col1"
    DT[, colname, with = FALSE] 
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    
  2. '점점'( ..) 접두사 :

    DT[, ..colname]    
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    

'점점'( ..) 표기법에 대한 자세한 설명은 1.10.2의 새로운 기능 (현재 도움말 텍스트에 설명되어 있지 않음)을 참조하십시오.

변수에 할당 하려면 LHS를 :=괄호로 묶습니다.

DT[, (colname) := 4:6]    
#    col1
# 1:    4
# 2:    5
# 3:    6

후자는 전체 열 벡터를 참조로 대체하기 때문에 열 plonk 라고합니다. 하위 집합 i이 있으면 참조로 하위 할당됩니다. 괄호 (colname)는 2014 년 10 월 CRAN 버전 v1.9.4에 도입 된 속기입니다. 다음은 뉴스 항목입니다 .

with = FALSEwith :=의 LHS :=를 괄호로 감싸는 것이 한동안 선호되었으므로 with 사용 은 이제 모든 경우에 사용되지 않습니다 .

colVar = "col1"
DT[, colVar := 1, with = FALSE]                 # deprecated, still works silently
DT[, (colVar) := 1]                             # please change to this
DT[, c("col1", "col2") := 1]                    # no change
DT[, 2:4 := 1]                                  # no change
DT[, c("col1","col2") := list(sum(a), mean(b)]  # no change
DT[, `:=`(...), by = ...]                       # no change

세부 정보 섹션을 참조하십시오 ?`:=`.

DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol

그리고 코멘트에서 추가 질문에 답하기 위해 한 가지 방법이 있습니다 (평소와 같이 여러 가지 방법이 있습니다) :

DT[, colname := cumsum(get(colname)), with = FALSE]
#    col1
# 1:    4
# 2:    9
# 3:   15 

또는, 당신은 쉽게 읽을 단지에 대한 쓰기 및 디버그 찾을 수 서버에 전송하는 동적 SQL 문을 생성 유사를 :evalpaste

expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"

eval(parse(text=expr))
#    col1
# 1:    4
# 2:   13
# 3:   28

그렇게 많이하면 도우미 함수를 정의 할 수 있습니다 EVAL.

EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))

EVAL("DT[,",colname,":=cumsum(",colname,")]")
#    col1
# 1:    4
# 2:   17
# 3:   45

이제 data.table1.8.2가 자동으로 j효율성을 최적화하므로이 eval방법 을 사용하는 것이 좋습니다 . get()에는 j예를 들어, 어떤 최적화를 방지 할 수 있습니다.

또는 set(). 오버 헤드가 적고 기능적인 형식 인 :=. 여기에서는 괜찮습니다. 을 참조하십시오 ?set.

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66

1
답장을 주셔서 감사합니다 Matthew. with = FALSE는 확실히 내 문제의 일부를 해결합니다. 그러나 실제로는 열을 열의 누적으로 바꾸고 싶습니다. 어떻게 든 할당의 오른쪽에있는 변수로 열 이름을 참조 할 수 있습니까?
frankc

Acutally, 나는 dt 내부에 존재하지 않고 잘 작동하는 다른 이름으로 외부에서 cumsum을 지정했습니다.
frankc

1
그러나 그것은 전체 추가 라인이 될 것입니다! 별로 우아하지 않습니다. :)하지만 가끔 유용합니다. 이 경우 변수 이름을로 시작 .하거나 앞으로 해당 기호를 열 이름으로 포함 할 ..경우 잠재적 인 마스킹을 방지하는 것이 가장 좋습니다 DT(열 이름이로 시작하지 않는 규칙을 고수 .). 추가 .()및 .NET 과 같은 범위 문제를보다 강력하게 만들기위한 몇 가지 기능 요청이 있습니다 ..().
Matt Dowle 2012 년

답변을 수정하신 것을 확인하기 전에 답변을 드렸습니다. 내 첫 번째 생각은 eval (parse ()) 이었지만 어떤 이유에서인지 외부에서 수행하는 것이 나에게 밝아 졌을 때 작동하는 데 어려움을 겪었습니다. 이것은 내가 생각하지 못한 많은 것들에 대한 훌륭한 대답입니다. 일반적으로 data.table에 감사드립니다. 훌륭한 패키지입니다.
frankc

2
fn$gsubfn 패키지 의 quasi-perl 유형 문자열 보간 을 사용하여 EVAL 솔루션의 가독성을 향상시킬 수 library(gsubfn); fn$EVAL( "DT[,$colname:=cumsum($colname)]" )있습니다..
G. Grothendieck

8

* 이것은 실제로 대답은 아니지만 댓글을 게시 할 거리의 신용이 충분하지 않습니다.

어쨌든, 변수에 저장된 이름으로 데이터 테이블에 새 열을 실제로 만들려는 사람을 위해 다음 작업을 수행해야합니다. 성능에 대한 단서가 없습니다. 개선을위한 제안 사항이 있습니까? 이름없는 새 열에 항상 V1이라는 이름이 부여된다고 가정하는 것이 안전합니까?

colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))

sum ()에서 잘 참조 할 수 있지만 동일한 단계에서 할당 할 수없는 것 같습니다. BTW,이 작업을 수행해야하는 이유는 colname이 Shiny 앱의 사용자 입력을 기반으로하기 때문입니다.


작업에 대한 +1 :이 작업을 수행하는 "방법"이 아니라는 데 동의합니다.하지만이 주제에 대한 모든 게시물에 45 분 정도 시간을 쏟았 기 때문에 실제로 도달 할 수 있었던 유일한 솔루션입니다. 작업-시간을내어 지적 해 주셔서 감사합니다!
neuropsych

도와 드릴 수있어서 기쁩니다! 불행히도이 3 라이너가 끔찍하지는 않지만 data.tables를 직접 사용하여 더 우아한 솔루션을 찾지 못했습니다. 내 시나리오에서는 사용자 입력을 기반으로 한 집합에서 선택하는 대신 항상 단일 열을 필터링 할 수 있기 때문에 데이터를 "넓게"대신 "길게"만들기 위해 더 간단한 대안을 사용하는 것이 더 간단하다는 것을 깨달았습니다. 열
efh0888

2
V1새 이름 이라고 가정하는 것은 안전하지 않습니다 . 예를 들어, 당신이 CSV 읽으면 fread과 익명의 열이, 그것은이됩니다 V1이름을 (그리고 read.csv줄 것이다 X). 따라서 테이블에 이미 V1. 어쩌면 단지로 이름을 얻을names(DT)[length(names(DT))]
dracodoc

2

여러 열 및 열 값에 적용된 함수의 경우.

함수에서 값을 업데이트 할 때 RHS는 목록 객체 여야하므로 .SDwith 루프를 사용 lapply하면 트릭을 수행 할 수 있습니다.

아래 예는 정수 열을 숫자 열로 변환합니다.

a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class)  # show classes of columns
#         a           b          c1 
# "integer"   "integer" "character" 

# column name character vector
nm <- c("a", "b")

# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]

sapply(a1, class)
#         a           b          c1 
# "numeric"   "numeric" "character" 

2

변수 또는 함수를 통해 data.table에서 여러 열을 검색합니다.

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]

모든 양보

   that whatever
1:    1        1
2:    2        2

나는 .SDcols가장 우아한 방법을 찾습니다 .


1

당신은 이것을 시도 할 수 있습니다

colname <-as.name ( "COL_NAME")

DT2 <-DT [, list (COL_SUM = sum (eval (colname, .SD))), 기준 = c (그룹)]


1
코드를 게시하는 대신 코드와 함께 설명을 추가하는 것이 좋습니다.
MBorg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.