web-dev-qa-db-ja.com

Rに相当するCaseステートメント

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

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

あなたが提供できる助けは大歓迎です!

69
Btibert3

casesパッケージのmemisc関数をご覧ください。 2つの異なる使用方法でケース機能を実装します。パッケージの例から:

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

ここで、xyは2つのベクトルです。

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

26
Henrico

2016年5月にdplyrに追加されたcase_when()は、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"
  )
)
25
Evan Cortens

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"))
21
Marek

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

これのマイナス面は、各アイテムのカテゴリ名(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
17

私見、最も簡単で普遍的なコード:

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'
})
14
Gregory Demin

「切り替え」の提案はありません。コード例(実行):

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

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動作を取得します。

7
42-

Carパッケージのrecodeを使用できます。

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

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

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

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

5
jamesM

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ではなく要素としてmutateから抜け出すことができる場合のボーナスポイント!一致しないすべての行をキャッチする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

switch()を参照している場合に使用します。制御ステートメントのように見えますが、実際には関数です。式が評価され、この値に基づいて、リスト内の対応するアイテムが返されます。

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

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

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

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

ケーススタイルの再マッピングタスクには、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('animal','animal','bird','bird')
)

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

Sqlに似た構文を使用する場合は、sqldfパッケージを使用できます。使用される関数の名前もsqldfであり、構文は次のとおりです。

sqldf(<your query in quotation marks>)
2
kuba

ここでは、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
Aaron