データフレームに変数があり、フィールドの1つに通常7〜8個の値があります。データフレーム内の新しい変数内で、3つまたは4つの新しいカテゴリをまとめたいと思います。最善のアプローチは何ですか?
SQLのようなツールを使用しているが、Rでこれを攻撃する方法がわからない場合は、CASEステートメントを使用します。
あなたが提供できるどんな助けも大いに感謝されるでしょう!
回答:
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"
)
)
.$
各列の前にある必要はありません。
.$
は不要になりました。この答えが最初に書かれたとき、それはそうでした。
switch
、ケースのキーの代わりに一連の式を作成できるため、この回答が気に入っています。
を見てください 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パッケージ、ケースの例
取得した場合は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"))
x
最後の行のはchangelevels
?
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
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'
})
y = 'else'
。それ以上の条件を満たさない要素は変更されません。
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
問題を引き起こす可能性のある動作が発生します。これは、ほとんどの人がファクターを文字として表示し、すべての関数がそれらをそのように処理すると誤って想定しているためです。
車のパッケージからrecodeを使用できます。
library(ggplot2) #get data
library(car)
daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]
sos::findFn("recode")
発見doBy::recodeVar
、epicalc::recode
、memisc::recode
、私は...詳細に彼らを見ていない
私はこれらのどれも好きではありません、それらは読者または潜在的なユーザーに明確ではありません。私は無名関数を使用しています。構文はcaseステートメントほど洗練されていませんが、評価はcaseステートメントに似ており、それほど面倒ではありません。これは、変数が定義されている場所で評価することも前提としています。
result <- ( function() { if (x==10 | y< 5) return('foo')
if (x==11 & y== 5) return('bar')
})()
これら()はすべて、無名関数を囲んで評価するために必要です。
result <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )
。2)これは、x
とy
がスカラーである場合にのみ機能します。ベクトルの場合、元の質問と同様に、ネストされたifelse
ステートメントが必要になります。
私はあなたが参照しているそれらの場合に使用していますswitch()
。制御文のように見えますが、実際は関数です。式が評価され、この値に基づいて、リスト内の対応する項目が返されます。
switchは、最初の引数が文字列と数値のどちらに評価されるかに応じて、2つの異なる方法で機能します。
以下は、古いカテゴリを新しいカテゴリに折りたたむという問題を解決する簡単な文字列の例です。
文字列形式の場合、名前付き値の後にデフォルトとして名前なし引数を1つ指定します。
newCat <- switch(EXPR = category,
cat1 = catX,
cat2 = catX,
cat3 = catY,
cat4 = catY,
cat5 = catZ,
cat6 = catZ,
"not available")
ここでは、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
結果は希望どおりです。
ミキシング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の代わりに要素として変異から抜け出すことができる場合のボーナスポイント!一致しないすべての行をキャッチする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
この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
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') ]
dput()
b)ベースR、dplyr、data.table、tidyverse ...のソリューションが必要ですか?を使用して、サンプルデータスニペットを投稿してください。