Rで同等のcaseステートメント


92

データフレームに変数があり、フィールドの1つに通常7〜8個の値があります。データフレーム内の新しい変数内で、3つまたは4つの新しいカテゴリをまとめたいと思います。最善のアプローチは何ですか?

SQLのようなツールを使用しているが、Rでこれを攻撃する方法がわからない場合は、CASEステートメントを使用します。

あなたが提供できるどんな助けも大いに感謝されるでしょう!


a)それらは整数、数値、カテゴリ、または文字列ですか?dput()b)ベースR、dplyr、data.table、tidyverse ...のソリューションが必要ですか?を使用して、サンプルデータスニペットを投稿してください。
smci 2018

回答:


39

case_when()2016年5月にdplyrに追加された、は、と同様の方法でこの問題を解決しmemisc::cases()ます。

例えば:

library(dplyr)
mtcars %>% 
  mutate(category = case_when(
    .$cyl == 4 & .$disp < median(.$disp) ~ "4 cylinders, small displacement",
    .$cyl == 8 & .$disp > median(.$disp) ~ "8 cylinders, large displacement",
    TRUE ~ "other"
  )
)

dplyr 0.7.0の時点で、

mtcars %>% 
  mutate(category = case_when(
    cyl == 4 & disp < median(disp) ~ "4 cylinders, small displacement",
    cyl == 8 & disp > median(disp) ~ "8 cylinders, large displacement",
    TRUE ~ "other"
  )
)

4
.$各列の前にある必要はありません。
kath 2017

1
はい、dplyr 0.7.0(2017年6月9日リリース)以降、.$は不要になりました。この答えが最初に書かれたとき、それはそうでした。
Evan Cortens 2017

素晴らしいソリューション。両方のステートメントが真の場合。2番目のものは最初のものを上書きしていますか?
JDP

1
@JdPSQLのCASEWHENと同じように機能するため、ステートメントは順番に評価され、結果が最初のTRUEステートメントになります。(したがって、上記の例では、最後にTRUEを入力しました。これは、デフォルト値として機能します。)
Evan Cortens 2018

とは異なりswitch、ケースのキーの代わりに一連の式を作成できるため、この回答が気に入っています。
ダニッド2018年

27

を見てください casesmemiscパッケージ関数を。これは、2つの異なる使用方法でケース機能を実装します。パッケージの例から:

z1=cases(
    "Condition 1"=x<0,
    "Condition 2"=y<0,# only applies if x >= 0
    "Condition 3"=TRUE
    )

ここでx、とyは2つのベクトルです。

参照:memiscパッケージケースの例


24

取得した場合はfactor、標準的な方法でレベルを変更できます。

df <- data.frame(name = c('cow','pig','eagle','pigeon'), 
             stringsAsFactors = FALSE)
df$type <- factor(df$name) # First step: copy vector and make it factor
# Change levels:
levels(df$type) <- list(
    animal = c("cow", "pig"),
    bird = c("eagle", "pigeon")
)
df
#     name   type
# 1    cow animal
# 2    pig animal
# 3  eagle   bird
# 4 pigeon   bird

ラッパーとして単純な関数を書くことができます:

changelevels <- function(f, ...) {
    f <- as.factor(f)
    levels(f) <- list(...)
    f
}

df <- data.frame(name = c('cow','pig','eagle','pigeon'), 
                 stringsAsFactors = TRUE)

df$type <- changelevels(df$name, animal=c("cow", "pig"), bird=c("eagle", "pigeon"))

2
いい答えだ。そのような古い名前と新しい名前のレベルへの引数としてリストを使用できることを忘れました。私の解決策は、レベルの順序をまっすぐに保つことに依存しているので、これはそのように優れています。
アーロンは2011

また、x最後の行のはchangelevels
アーロンは2011

22

switchステートメントを使用する方法は次のとおりです。

df <- data.frame(name = c('cow','pig','eagle','pigeon'), 
                 stringsAsFactors = FALSE)
df$type <- sapply(df$name, switch, 
                  cow = 'animal', 
                  pig = 'animal', 
                  eagle = 'bird', 
                  pigeon = 'bird')

> df
    name   type
1    cow animal
2    pig animal
3  eagle   bird
4 pigeon   bird

これの1つの欠点はanimal、各アイテムのカテゴリ名(など)を書き続ける必要があることです。以下のようにカテゴリを定義できる方が構文的に便利です(非常によく似た質問を参照してください。Rのデータフレームに列を追加するにはどうすればよいですか)。

myMap <- list(animal = c('cow', 'pig'), bird = c('eagle', 'pigeon'))

そして、どういうわけかこのマッピングを「反転」させたいのです。私は自分のinvMap関数を書きます:

invMap <- function(map) {
  items <- as.character( unlist(map) )
  nams <- unlist(Map(rep, names(map), sapply(map, length)))
  names(nams) <- items
  nams
}

次に、上記のマップを次のように反転します。

> invMap(myMap)
     cow      pig    eagle   pigeon 
"animal" "animal"   "bird"   "bird" 

そして、これを使用typeしてデータフレームに列を追加するのは簡単です。

df <- transform(df, type = invMap(myMap)[name])

> df
    name   type
1    cow animal
2    pig animal
3  eagle   bird
4 pigeon   bird

19

「スイッチ」の提案はありません。コード例(実行):

x <- "three"
y <- 0
switch(x,
       one = {y <- 5},
       two = {y <- 12},
       three = {y <- 432})
y

15

Imho、最も単純で普遍的なコード:

dft=data.frame(x = sample(letters[1:8], 20, replace=TRUE))
dft=within(dft,{
    y=NA
    y[x %in% c('a','b','c')]='abc'
    y[x %in% c('d','e','f')]='def'
    y[x %in% 'g']='g'
    y[x %in% 'h']='h'
})

私はこの方法が好きです。ただし、状況によってはこれが不可欠であるため、「その他」の実装はありますか
T.Fung19年

2
@ T.Fung最初の行をに変更できますy = 'else'。それ以上の条件を満たさない要素は変更されません。
グレゴリーデ

7

switch声明はありますが、思ったとおりに機能させることはできません。例を提供していないので、因子変数を使用して例を作成します。

 dft <-data.frame(x = sample(letters[1:8], 20, replace=TRUE))
 levels(dft$x)
[1] "a" "b" "c" "d" "e" "f" "g" "h"

再割り当てに適した順序で必要なカテゴリを指定する場合は、因子変数または数値変数をインデックスとして使用できます。

c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x]
 [1] "def" "h"   "g"   "def" "def" "abc" "h"   "h"   "def" "abc" "abc" "abc" "h"   "h"   "abc"
[16] "def" "abc" "abc" "def" "def"

dft$y <- c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x] str(dft)
'data.frame':   20 obs. of  2 variables:
 $ x: Factor w/ 8 levels "a","b","c","d",..: 4 8 7 4 6 1 8 8 5 2 ...
 $ y: chr  "def" "h" "g" "def" ...

後で、実際には2つの異なるスイッチ機能があることを知りました。これは汎用関数ではありませんが、switch.numericまたはのいずれかとして考える必要がありますswitch.character。最初の引数がR 'ファクター'の場合、switch.numeric問題を引き起こす可能性のある動作が発生します。これは、ほとんどの人がファクターを文字として表示し、すべての関数がそれらをそのように処理すると誤って想定しているためです。


6

車のパッケージからrecodeを使用できます。

library(ggplot2) #get data
library(car)
daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]

11
テキストからパラメータを解析する関数をサポートできません
hadley 2011年

はい、でも誰かがもっと良いバージョンを書いたかどうか知っていますか?sos::findFn("recode")発見doBy::recodeVarepicalc::recodememisc::recode、私は...詳細に彼らを見ていない
ベンBolker

5

私はこれらのどれも好きではありません、それらは読者または潜在的なユーザーに明確ではありません。私は無名関数を使用しています。構文はcaseステートメントほど洗練されていませんが、評価はcaseステートメントに似ており、それほど面倒ではありません。これは、変数が定義されている場所で評価することも前提としています。

result <- ( function() { if (x==10 | y< 5) return('foo') 
                         if (x==11 & y== 5) return('bar')
                        })()

これら()はすべて、無名関数を囲んで評価するために必要です。


6
1)機能部分は不要です。あなたはただすることができますresult <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )。2)これは、xyがスカラーである場合にのみ機能します。ベクトルの場合、元の質問と同様に、ネストされたifelseステートメントが必要になります。
アーロンは2011

4

私はあなたが参照しているそれらの場合に使用していますswitch()。制御文のように見えますが、実際は関数です。式が評価され、この値に基づいて、リスト内の対応する項目が返されます。

switchは、最初の引数が文字列と数値のどちらに評価されるかに応じて、2つの異なる方法で機能します。

以下は、古いカテゴリを新しいカテゴリに折りたたむという問題を解決する簡単な文字列の例です。

文字列形式の場合、名前付き値の後にデフォルトとして名前なし引数を1つ指定します。

newCat <- switch(EXPR = category,
       cat1   = catX,
       cat2   = catX,
       cat3   = catY,
       cat4   = catY,
       cat5   = catZ,
       cat6   = catZ,
       "not available")

3

SQLのような構文が必要な場合は、sqldfパッケージを使用できます。使用する関数も名前でsqldfあり、構文は次のとおりです。

sqldf(<your query in quotation marks>)

2

ここでは、caseステートメントは実際には正しいアプローチではない可能性があります。これが要因である可能性が高い場合は、要因のレベルを適切に設定するだけです。

このように、AからEの文字の因数があるとします。

> a <- factor(rep(LETTERS[1:5],2))
> a
 [1] A B C D E A B C D E
Levels: A B C D E

レベルBとCを結合してBCという名前を付けるには、それらのレベルの名前をBCに変更するだけです。

> levels(a) <- c("A","BC","BC","D","E")
> a
 [1] A  BC BC D  E  A  BC BC D  E 
Levels: A BC D E

結果は希望どおりです。


2

ミキシングplyr::mutatedplyr::case_when私のために動作し、読みやすいです。

iris %>%
plyr::mutate(coolness =
     dplyr::case_when(Species  == "setosa"     ~ "not cool",
                      Species  == "versicolor" ~ "not cool",
                      Species  == "virginica"  ~ "super awesome",
                      TRUE                     ~ "undetermined"
       )) -> testIris
head(testIris)
levels(testIris$coolness)  ## NULL
testIris$coolness <- as.factor(testIris$coolness)
levels(testIris$coolness)  ## ok now
testIris[97:103,4:6]

列がcharの代わりに要素として変異から抜け出すことができる場合のボーナスポイント!一致しないすべての行をキャッチするcase_whenステートメントの最後の行は非常に重要です。

     Petal.Width    Species      coolness
 97         1.3  versicolor      not cool
 98         1.3  versicolor      not cool  
 99         1.1  versicolor      not cool
100         1.3  versicolor      not cool
101         2.5  virginica     super awesome
102         1.9  virginica     super awesome
103         2.1  virginica     super awesome

2

このbase関数はmerge、ケーススタイルの再マッピングタスクに使用できます。

df <- data.frame(name = c('cow','pig','eagle','pigeon','cow','eagle'), 
                 stringsAsFactors = FALSE)

mapping <- data.frame(
  name=c('cow','pig','eagle','pigeon'),
  category=c('mammal','mammal','bird','bird')
)

merge(df,mapping)
# name category
# 1    cow   mammal
# 2    cow   mammal
# 3  eagle     bird
# 4  eagle     bird
# 5    pig   mammal
# 6 pigeon     bird

1

data.table v1.13.0以降、関数fcase()(fast-case)を使用してSQLのようなCASE操作を実行できます(これも同様ですdplyr::case_when())。

require(data.table)

dt <- data.table(name = c('cow','pig','eagle','pigeon','cow','eagle'))
dt[ , category := fcase(name %in% c('cow', 'pig'), 'mammal',
                        name %in% c('eagle', 'pigeon'), 'bird') ]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.