Kendi fonksiyonunuzu yazarken R'nin üç nokta özelliği nasıl kullanılır?


186

R dili, değişken sayıda argüman alabilen işlevleri tanımlamak için şık bir özelliğe sahiptir. Örneğin, işlev data.frameistenilen sayıda bağımsız değişkeni alır ve her bağımsız değişken, sonuçta elde edilen veri tablosundaki bir sütunun verileri olur. Örnek kullanım:

> data.frame(letters=c("a", "b", "c"), numbers=c(1,2,3), notes=c("do", "re", "mi"))
  letters numbers notes
1       a       1    do
2       b       2    re
3       c       3    mi

İşlevin imzası aşağıdaki gibi bir üç nokta içerir:

function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, 
    stringsAsFactors = default.stringsAsFactors()) 
{
    [FUNCTION DEFINITION HERE]
}

Benzer bir şey yapan, birden çok değer alarak ve tek bir dönüş değerinde (ve diğer bazı işlemleri yaparken) birleştirerek bir işlev yazmak istiyorum. Bunu yapmak için ..., fonksiyonun içindeki fonksiyonun argümanlarından nasıl "paketten çıkarılacağını" anlamaya ihtiyacım var. Bunu nasıl yapacağımı bilmiyorum. İşlevi tanımında ilgili çizgi data.frameolduğunu object <- as.list(substitute(list(...)))[-1L]ben bir anlam yapamayan,.

Peki, üç noktayı işlevin imzasından örneğin bir listeye nasıl dönüştürebilirim?

Daha spesifik olmak gerekirse, get_list_from_ellipsisaşağıdaki koda nasıl yazabilirim ?

my_ellipsis_function(...) {
    input_list <- get_list_from_ellipsis(...)
    output_list <- lapply(X=input_list, FUN=do_something_interesting)
    return(output_list)
}

my_ellipsis_function(a=1:10,b=11:20,c=21:30)

Düzenle

Öyle görünüyor ki bunu yapmanın iki yolu var. Onlar as.list(substitute(list(...)))[-1L]ve list(...). Ancak, bu ikisi tam olarak aynı şeyi yapmaz. (Farklılıklar için cevaplardaki örneklere bakın.) Herkes bana aralarındaki pratik farkın ne olduğunu ve hangisini kullanmam gerektiğini söyleyebilir mi?

Yanıtlar:


113

Cevapları ve yorumları okudum ve birkaç şeyden bahsedilmediğini görüyorum:

  1. data.framelist(...)sürümü kullanır . Kodun parçası:

    object <- as.list(substitute(list(...)))[-1L]
    mrn <- is.null(row.names)
    x <- list(...)

    objectsütun adlarıyla biraz sihir yapmak için kullanılır, ancak xfinal oluşturmak için kullanılır data.frame.
    Değerlendirilmemiş ...argümanın kullanımı için kullanılan write.csvkoda bakın match.call.

  2. Yorum yazarken Dirk cevabı sonucu listelerin bir listesi değildir. Uzunlukların bir listesidir, hangi elemanlar languagetiptir. İlk nesne bir symbol- list, ikincisi ifade 1:10vb. Bu neden [-1L]gerekli olduğunu açıklar : beklenen symbolargümanlardan beklenenleri kaldırır ...(çünkü her zaman bir listedir).
    Dirk devletleri substitute"ağaç değerlendirilmemiş ayrıştırmayı ifade" döndürür.
    Aradığınızda my_ellipsis_function(a=1:10,b=11:20,c=21:30)ardından ...bağımsız değişkenlerin bir listesini "oluşturur": list(a=1:10,b=11:20,c=21:30)ve substituteo dört elementin bir listesini yapmak:

    List of 4
    $  : symbol list
    $ a: language 1:10
    $ b: language 11:20
    $ c: language 21:30

    İlk öğenin bir adı yok ve bu [[1]]Dirk cevabında. Bu sonuçları kullanarak elde:

    my_ellipsis_function <- function(...) {
      input_list <- as.list(substitute(list(...)))
      str(input_list)
      NULL
    }
    my_ellipsis_function(a=1:10,b=11:20,c=21:30)
  3. Yukarıdaki gibi str, bir fonksiyonda hangi nesnelerin bulunduğunu kontrol etmek için kullanabiliriz .

    my_ellipsis_function <- function(...) {
        input_list <- list(...)
        output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
        return(output_list)
    }
    my_ellipsis_function(a=1:10,b=11:20,c=21:30)
     int [1:10] 1 2 3 4 5 6 7 8 9 10
     int [1:10] 11 12 13 14 15 16 17 18 19 20
     int [1:10] 21 22 23 24 25 26 27 28 29 30
    $a
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
       1.00    3.25    5.50    5.50    7.75   10.00 
    $b
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
       11.0    13.2    15.5    15.5    17.8    20.0 
    $c
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
       21.0    23.2    25.5    25.5    27.8    30.0 

    Tamam. Sürüme bakalım substitute:

       my_ellipsis_function <- function(...) {
           input_list <- as.list(substitute(list(...)))
           output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
           return(output_list)
       }
       my_ellipsis_function(a=1:10,b=11:20,c=21:30)
        symbol list
        language 1:10
        language 11:20
        language 21:30
       [[1]]
       Length  Class   Mode 
            1   name   name 
       $a
       Length  Class   Mode 
            3   call   call 
       $b
       Length  Class   Mode 
            3   call   call 
       $c
       Length  Class   Mode 
            3   call   call 

    İhtiyacımız olan bu değil. Bu tür nesnelerle başa çıkmak için ek numaralara ihtiyacınız olacak (olduğu gibi write.csv).

Kullanmak istiyorsanız ...o zaman Shane cevapta olduğu gibi kullanmalısınız list(...).


38

Üç noktayı bir listeye dönüştürebilir list()ve ardından üzerinde işlemlerinizi gerçekleştirebilirsiniz:

> test.func <- function(...) { lapply(list(...), class) }
> test.func(a="b", b=1)
$a
[1] "character"

$b
[1] "numeric"

Yani get_list_from_ellipsisfonksiyonunuz bundan başka bir şey değildir list.

Bunun için geçerli bir kullanım durumu, işlem için bilinmeyen sayıda nesneyi ( c()veya örneğinizde olduğu gibi) iletmek istediğiniz durumlardır data.frame(). ...Bununla birlikte, her bir parametreyi önceden bildiğinizde kullanmak iyi bir fikir değildir , çünkü argüman dizesine biraz belirsizlik ve daha fazla karmaşıklık ekler (ve işlev imzasını başka herhangi bir kullanıcıya açık hale getirmez). Bağımsız değişken listesi, işlev kullanıcıları için önemli bir belge parçasıdır.

Aksi takdirde, parametreleri kendi işlev argümanlarınızda göstermeden bir alt işleve geçmek istediğiniz durumlarda da kullanışlıdır. Bu, işlev belgelerinde belirtilebilir.


Üç noktayı alt işlevlere yönelik argümanlar için bir geçiş olarak kullanmayı biliyorum, ancak üç ilkel arasında elipsleri tanımladığım şekilde kullanmak da yaygın bir uygulamadır. Aslında, hem listve cişlevleri bu şekilde çalışır, ancak her ikisi de ilkeldir, bu yüzden nasıl çalıştıklarını anlamak için kaynak kodlarını kolayca inceleyemiyorum.
Ryan

rbind.data.framebu şekilde kullanın.
Marek

5
Eğer list(...)yeterliyse, R gibi yerleşimler neden data.framedaha uzun formu as.list(substitute(list(...)))[-1L]kullanıyor?
Ryan

1
Ben yaratmadı gibi data.frame, bunu cevabını bilmiyorum (dedi, emin oradaki değilim olduğunu bunun için iyi bir neden). Kullandığım list()kendi ambalajlarda bu amaçla ve onunla bir sorunla karşılaşmaya henüz.
Shane

34

Sadece Shane ve Dirk'ün yanıtlarına eklemek için: karşılaştırmak ilginç

get_list_from_ellipsis1 <- function(...)
{
  list(...)
}
get_list_from_ellipsis1(a = 1:10, b = 2:20) # returns a list of integer vectors

$a
 [1]  1  2  3  4  5  6  7  8  9 10

$b
 [1]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

ile

get_list_from_ellipsis2 <- function(...)
{
  as.list(substitute(list(...)))[-1L]
}
get_list_from_ellipsis2(a = 1:10, b = 2:20) # returns a list of calls

$a
1:10

$b
2:20

Bu haliyle, her iki sürüm de amaçlarınız için uygun görünüyor my_ellipsis_function, ancak birincisi açıkça daha basit.


15

Cevabın yarısını zaten verdin. Düşünmek

R> my_ellipsis_function <- function(...) {
+   input_list <- as.list(substitute(list(...)))
+ }
R> print(my_ellipsis_function(a=1:10, b=2:20))
[[1]]
list

$a
1:10

$b
11:20

R> 

Bu, iki argüman aldı ave bçağrıdan aldı ve bir listeye dönüştürdü. İstediğiniz bu değil miydi?


2
Tam olarak istediğim gibi değil. Bu aslında bir liste listesi gibi görünüyor. Dikkat edin [[1]]. Ayrıca, büyülü büyünün nasıl as.list(substitute(list(...)))çalıştığını bilmek istiyorum .
Ryan

2
İç argümanlara dayanarak list(...)bir listnesne yaratır . Sonra substitute()değerlendirilmemiş ifade için ayrıştırma ağacını oluşturur; bu işlevin yardımına bakın. R (veya S) üzerinde iyi bir gelişmiş metnin yanı sıra. Bu önemsiz şeyler değil.
Dirk Eddelbuettel

Tamam, ne olacak [[-1L]](sorumdan)? Olmamalı mı [[1]]?
Ryan

3
Dizin oluşturmayı okumalısınız. Eksi "hariç tut" anlamına gelir, yani print(c(1:3)[-1])yalnızca 2 ve 3 yazdıracaktır. LBu R kaynaklarda çok yapılıyor, bir tamsayı olarak biter sağlamak için yeni-fangled yoludur.
Dirk Eddelbuettel

7
Ben endeksleme kadar okumak gerekmez, ama yok sen gösteriyor ki komutların çıkışına yakın dikkat etmeleri gerekir. Arasındaki fark [[1]]ve $aendeksleri beni iç içe listeleri ilgisi olduğunu düşündürdü. Ama şimdi anladığım kadarıyla istediğim liste, ama ön tarafta fazladan bir unsur var. O zaman [-1L]mantıklı. Zaten bu ekstra ilk unsur nereden geliyor? Bunu basitçe kullanmak yerine kullanmamın bir nedeni var list(...)mı?
Ryan

6

Bu beklendiği gibi çalışır. Etkileşimli bir oturum aşağıdaki gibidir:

> talk <- function(func, msg, ...){
+     func(msg, ...);
+ }
> talk(cat, c("this", "is", "a","message."), sep=":")
this:is:a:message.
> 

Aynı, varsayılan bir argüman dışında:

> talk <- function(func, msg=c("Hello","World!"), ...){
+     func(msg, ...);
+ }
> talk(cat,sep=":")
Hello:World!
> talk(cat,sep=",", fill=1)
Hello,
World!
>

Gördüğünüz gibi, varsayılanlar belirli bir durumda istediğiniz gibi değilse, işleviniz içindeki bir işleve 'ekstra' bağımsız değişkenler iletmek için bunu kullanabilirsiniz.

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.