フィールドの1つが通常7〜8の値を持つデータフレームに変数があります。データフレーム内の新しい変数内で3つまたは4つの新しいカテゴリを折りたたみます。最善のアプローチは何ですか?
SQLのようなツールを使用しているが、Rでこれを攻撃する方法がわからない場合は、CASEステートメントを使用します。
あなたが提供できる助けは大歓迎です!
cases
パッケージのmemisc
関数をご覧ください。 2つの異なる使用方法でケース機能を実装します。パッケージの例から:
z1=cases(
"Condition 1"=x<0,
"Condition 2"=y<0,# only applies if x >= 0
"Condition 3"=TRUE
)
ここで、x
とy
は2つのベクトルです。
参照: memiscパッケージ 、 ケースの例
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"
)
)
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"))
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
私見、最も簡単で普遍的なコード:
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'
})
「切り替え」の提案はありません。コード例(実行):
x <- "three";
y <- 0;
switch(x,
one = {y <- 5},
two = {y <- 12},
three = {y <- 432})
y
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
動作を取得します。
Carパッケージのrecodeを使用できます。
library(ggplot2) #get data
library(car)
daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]
私はこれらのどれも好きではありません、彼らは読者または潜在的なユーザーに明確ではありません。私は単に匿名関数を使用しています。構文はcaseステートメントほど滑らかではありませんが、評価はcaseステートメントに似ており、それほど苦痛ではありません。これは、変数が定義されている場所で評価することも想定しています。
result <- ( function() { if (x==10 | y< 5) return('foo')
if (x==11 & y== 5) return('bar')
})()
これらすべて()は、匿名関数を囲み、評価するために必要です。
plyr::mutate
とdplyr::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")
ケーススタイルの再マッピングタスクには、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
Sqlに似た構文を使用する場合は、sqldf
パッケージを使用できます。使用される関数の名前もsqldf
であり、構文は次のとおりです。
sqldf(<your query in quotation marks>)
ここでは、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
結果は望み通りです。