Değişken adları bir karakter vektöründe depolandığında data.table'ı seçin / atayın


92

data.tableDeğişken adları bir karakter vektöründe saklanıyorsa, a'daki değişkenlere nasıl başvurursunuz ? Örneğin, bu bir data.frame:

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

Aynı işlemi bir data.table için :=notasyonlu veya notasız olarak nasıl gerçekleştirebilirim ? Bariz olan şey dt[ , list(colname)]işe yaramıyor (ne de olmasını beklemiyordum).

Yanıtlar:


133

Değişkenleri programlı olarak seçmenin iki yolu :

  1. with = FALSE:

     DT = data.table(col1 = 1:3)
     colname = "col1"
     DT[, colname, with = FALSE] 
     #    col1
     # 1:    1
     # 2:    2
     # 3:    3
    
  2. 'nokta nokta' ( ..) öneki:

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

'Nokta nokta' ( ..) gösteriminin daha fazla açıklaması için, 1.10.2'deki Yeni Özellikler'e bakın (şu anda yardım metninde açıklanmamaktadır).

To atamak değişken (ler) kadar olan LHS sarmak :=parantez içinde:

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

İkincisi, sütun çizimi olarak bilinir , çünkü tüm sütun vektörünü referansla değiştirirsiniz. Bir alt küme varsa i, referansa göre alt atanır. Etrafında parens (colname)CRAN Eki üzerinde versiyon v1.9.4 tanıtılan bir kısaltmadır 2014 İşte haber :

Kullanımı with = FALSEile :=artık her durumda kullanımdan kaldırılmıştır ait LHS sarma göz önüne alındığında :=parantez ile bir süre tercih edilmiştir.

colVar = "col1"
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

Ayrıca şuradaki Ayrıntılar bölümüne bakın ?`:=`:

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

Ve yorumda daha fazla soruyu yanıtlamak için işte bir yol var (her zamanki gibi birçok yol vardır):

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

ya, sen daha kolay okumak için sadece yazı göndermekte ve hata ayıklama bulabilir evalbir pastebir sunucuya göndermek için dinamik bir SQL deyimi inşa benzer,:

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

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

Bunu çok yaparsanız, bir yardımcı işlev tanımlayabilirsiniz EVAL:

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

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

Şimdi bu data.table1.8.2 otomatik olarak optimize jverimlilik için, kullanımı tercih edilebilir evalbir yöntem. get()İçerisinde j, örneğin, bazı en iyi duruma engel olur.

Veya var set(). Düşük ek yük, işlevsel bir form, :=burada sorun olmaz. Bakın ?set.

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

1
Cevap için teşekkürler Matthew. İle = YANLIŞ, sorunumun bir bölümünü kesinlikle çözer. Gerçekte, sütunu sütunun toplamı ile değiştirmek istiyorum. Sütun adını bir şekilde atamanın sağ tarafındaki değişkene göre referans alabilir miyim?
frankc

Aslında, cumsum'u dt'nin içinde olmayan ve iyi çalışan farklı bir adla harici olarak depoladım.
frankc

1
Ama bu tamamen ekstra bir çizgi olurdu! Çok zarif değil :) Ama tamam bazen faydalı oluyor. Bu durumlarda, değişken adını en iyisi ile başlatmak .veya ..herhangi bir potansiyel maskelemeden kaçınmak için DT, bu sembolü gelecekte bir sütun adı olarak içerdiyse (ve sütun adlarının başlamadığı kurala sadık kalın .). İlave etme gibi böyle kapsamı ilişkin sorunlara daha sağlam hale getirmek için bazı özellik istekleri vardır .()ve ..().
Matt Dowle

Cevabınızı düzenlediğinizi fark etmeden önce cevap verdim. İlk düşüncem eval (parse ()) idi, ancak nedense onu çalıştırmakta güçlük çekiyordum, bunu sadece dışarıdan yapmam gerektiğini anladığımda. Bu, düşünmediğim birçok şeyi içeren harika bir cevap. Genel olarak data.table için teşekkürler, bu harika bir paket.
frankc

2
Eğer yarı perl tipi dize interpolasyon kullanabileceği Not fn$gsubfn paketten EVAL çözümün okunabilirliği artırmak için: library(gsubfn); fn$EVAL( "DT[,$colname:=cumsum($colname)]" ).
G. Grothendieck

8

* Bu gerçekten bir cevap değil, ancak yorum yazmak için yeterince sokak kredim yok: /

Her neyse, bir veri tablosunda bir değişkende saklanan bir adla yeni bir sütun oluşturmak isteyen herkes için aşağıdakileri yapmalıyım. Performansı hakkında hiçbir fikrim yok. İyileştirme için herhangi bir öneriniz var mı? İsimsiz yeni bir sütuna her zaman V1 adının verileceğini varsaymak güvenli midir?

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

Dikkat edin, sum () 'da gayet iyi başvurabilirim, ancak aynı adımda atamak için alamıyorum. BTW, bunu yapmamın nedeni colname Shiny uygulamasındaki kullanıcı girdisine dayanacak.


+1 sadece çalışmak için: Bunu yapmanın "yolu" olmamasına katılıyorum, ancak bu konudaki her SO gönderisinin üzerine 45 dakika kadar harcadıktan sonra, gerçekten ulaşabildiğim tek çözüm bu. iş - bunu belirtmek için zaman ayırdığınız için teşekkürler!
nöropsik

Yardımcı olduğuma sevindim! Ne yazık ki, bu 3 liner korkunç olmasa da, doğrudan data.tables kullanarak daha zarif bir çözüm bulamadım. Senaryomda, daha basit bir alternatifin, verilerimi "geniş" yerine "uzun" yapmak için tidyr kullanmak olacağını fark ettim, çünkü kullanıcı girdisine dayalı olarak, bir kümeden seçim yapmak yerine her zaman tek bir sütuna göre filtre uygulayabilirdim sütun sayısı.
efh0888

2
V1Yeni adın olduğunu varsaymak güvenli değil . Örneğin, csv'yi ile okursanız freadve adsız bir sütun varsa, V1adı read.csvolacaktır (ve verecektir X). Yani masanızın zaten bir V1. Belki sadece adını alnames(DT)[length(names(DT))]
dracodoc

2

Birden çok sütun ve sütun değerlerine uygulanan bir işlev için.

Bir fonksiyonu değerleri güncellerken, RHS vb bir döngü kullanarak, bir liste nesnesi olmalıdır .SDile lapplyhile olacaktır.

Aşağıdaki örnek, tam sayı sütunlarını sayısal sütunlara dönüştürür

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'dan değişken veya işlev yoluyla birden çok sütun alın:

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

hangisi verir

   that whatever
1:    1        1
2:    2        2

.SDcolsEn zarif yolu buluyorum .


1

Bunu deneyebilirsin

colname <- as.name ("COL_NAME")

DT2 <- DT [, liste (COL_SUM = sum (eval (colname, .SD))), by = c (grup)]


1
Yalnızca kod göndermek yerine kodunuzla her zaman bir açıklama eklemeniz önerilir.
MBorg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.