`levels <-`(これはどんな魔術ですか?


114

別の質問への回答として、@ Marekは次のソリューションを投稿しました:https ://stackoverflow.com/a/10432263/636656

dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L, 
                                  7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, -20L), class = "data.frame")

`levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

これは出力として生成されます:

 [1] Generic Generic Bayer   Bayer   Advil   Tylenol Generic Advil   Bayer   Generic Advil   Generic Advil   Tylenol
[15] Generic Bayer   Generic Advil   Bayer   Bayer  

これは単なるベクターの出力なので、それを保存することでさらに混乱を招く可能性があります。

res <- `levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

明らかにこれはレベル関数へのある種の呼び出しですが、私はここで何が行われているのかわかりません。この種のソーサリーの用語は何ですか?このドメインで私の魔法能力をどのように増加させるのですか?


1
ともnames<-あり[<-ます。
2012年

1
また、私は他の質問でこれについて疑問に思いましたが、尋ねませんでした:structure(...)単に構造の代わりに何か理由がありますdata.frame(product = c(11L, 11L, ..., 8L))か?(そこで何か魔法が起こっているなら、私もそれを使いたいです!)
huon

2
これは"levels<-"関数の呼び出しです:function (x, value) .Primitive("levels<-")、のようなものX %in% Yはの省略形です"%in%"(X, Y)
BenBarnes、2012年

2
再現可能な例については、非常に便利な@dbaupp:stackoverflow.com/questions/5963269/...
アリB.フリードマン

8
なぜ誰かがこれを建設的でないと締めくくるのに投票したのか分かりませんか?Qには非常に明確な答えがあります。例で使用されている構文の意味は何ですか?これはRでどのように機能しますか?
Gavin Simpson

回答:


104

ここでの答えは良いですが、重要な点が欠けています。それについて説明してみましょう。

Rは関数型言語であり、そのオブジェクトを変更したくない。しかし、置換関数を使用して、代入ステートメントを許可します。

levels(x) <- y

に相当

x <- `levels<-`(x, y)

コツは、この書き換えはによって行われること<-です。それはによって行われませんlevels<-levels<-入力を受け取り、出力を与える通常の関数です。何も変化しません。

その結果の1つは、上記のルールに従って、<-再帰的でなければならないということです。

levels(factor(x)) <- y

です

factor(x) <- `levels<-`(factor(x), y)

です

x <- `factor<-`(x, `levels<-`(factor(x), y))

この純粋な関数型変換(割り当てが発生する最後まで)が、命令型言語での割り当てと同等であることは、一種の美しさです。私が正しく覚えている場合、関数型言語でのこの構造はレンズと呼ばれます。

しかし、その後、のような置換関数を定義するとlevels<-、別の予期しないウィンドフォールが発生します。割り当てを行うだけの機能ではなく、因子を取り込んで、異なるレベルの別の因子を出力する便利な関数があります。それについての「割り当て」は本当に何もありません!

したがって、あなたが記述しているコードは、の他のこの解釈を利用しているだけですlevels<-。名前levels<-が割り当てを示唆しているため、名前が少し紛らわしいことは認めますが、これは現在起こっていることではありません。コードは、一種のパ​​イプラインを設定するだけです。

  • 皮切りに dat$product

  • 係数に変換します

  • レベルを変更する

  • それを保管する res

個人的には、コード行は美しいと思います;)


33

ソーサリーはありません。これが(サブ)割り当て関数の定義方法です。 levels<-要素自体ではなく、因子の属性を(サブ)に割り当てるためのプリミティブなので、少し異なります。このタイプの関数の例はたくさんあります:

`<-`              # assignment
`[<-`             # sub-assignment
`[<-.data.frame`  # sub-assignment data.frame method
`dimnames<-`      # change dimname attribute
`attributes<-`    # change any attributes

他の二項演算子も同様に呼び出すことができます:

`+`(1,2)  # 3
`-`(1,2)  # -1
`*`(1,2)  # 2
`/`(1,2)  # 0.5

あなたはそれを知っているので、このようなものは本当にあなたの心を打つはずです:

Data <- data.frame(x=1:10, y=10:1)
names(Data)[1] <- "HI"              # How does that work?!? Magic! ;-)

1
通常の方法ではなく、そのように関数を呼び出すことが理にかなっている場合についてもう少し説明できますか?リンクされた質問で@Marekの例に取り組んでいますが、より明確な説明があると役に立ちます。
ドリュースティーン

4
@DrewSteen:コードの明快さ/読みやすさの理由から、はと`levels<-`(foo,bar)同じであるため、意味がありませんlevels(foo) <- bar。@Marekの例を使用する`levels<-`(as.factor(foo),bar)と、と同じfoo <- as.factor(foo); levels(foo) <- barです。
Joshua Ulrich

いいリスト。あなたlevels<-は本当にの省略形だと思っていませんattr<-(x, "levels") <- valueか、少なくとも、それがプリミティブに変換されてCコードに渡されるまでだったと思いませんか。
IRTFM 2014年

30

その「魔法」の理由は、「割り当て」フォームが機能するための実際の変数を持っている必要があるためです。そして、factor(dat$product)何にも割り当てられませんでした。

# This works since its done in several steps
x <- factor(dat$product)
levels(x) <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
x

# This doesn't work although it's the "same" thing:
levels(factor(dat$product)) <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
# Error: could not find function "factor<-"

# and this is the magic work-around that does work
`levels<-`(
  factor(dat$product),
  list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
  )

+1最初に因子に変換し、a within()を介してレベルを置き換え、transform()このように変更されたオブジェクトが返されて割り当てられると、よりクリーンになると思います。
Gavin Simpson

4
@GavinSimpson-同意する、私は魔法を説明するだけであり、私はそれを守らない;-)
Tommy

16

ユーザーコードの場合、なぜそのような言語操作が使用されるのでしょうか?あなたはこれがどんな魔法であるかを尋ね、他の人はあなたが名前を持つ置換関数を呼び出していると指摘しましたlevels<-。ほとんどの人にとってこれは魔法であり、実際の使用目的はですlevels(foo) <- bar

表示されるユースケースは異なります。productグローバル環境には存在しないため、呼び出しのローカル環境にしか存在しないlevels<-ため、実行したい変更は持続せず、の再割り当てはありませんでしたdat

これらの状況でwithin() は、は理想的な関数です。あなたは自然に書きたいと思うでしょう

levels(product) <- bar

Rではもちろんproductオブジェクトとして存在しません。within()Rコードを実行する環境を設定し、その環境内で式を評価するため、この問題を回避できます。within()したがって、呼び出しからの戻りオブジェクトの割り当ては、適切に変更されたデータフレームで成功します。

ここに例があります(新しく作成する必要はありませんdatX-私はそれを行うだけなので、中間ステップは最後に残ります)

## one or t'other
#dat2 <- transform(dat, product = factor(product))
dat2 <- within(dat, product <- factor(product))

## then
dat3 <- within(dat2, 
               levels(product) <- list(Tylenol=1:3, Advil=4:6, 
                                       Bayer=7:9, Generic=10:12))

それは与える:

> head(dat3)
  product
1 Generic
2 Generic
3   Bayer
4   Bayer
5   Advil
6 Tylenol
> str(dat3)
'data.frame':   20 obs. of  1 variable:
 $ product: Factor w/ 4 levels "Tylenol","Advil",..: 4 4 3 3 2 1 4 2 3 4 ...

私が示すような構成が大部分の場合にどのように役立つかを確認するのに苦労しています-データを変更したい場合は、データを変更し、別のコピーを作成せずに変更してください(これは結局のところlevels<-呼び出しがすべて行っていることです) )。

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