केस स्टेटमेंट समतुल्य आर


87

मेरे पास एक डेटाफ़्रेम में एक चर है जहां खेतों में से एक में आमतौर पर 7-8 मान होते हैं। मैं डेटाफ़्रेम के भीतर एक नए चर के भीतर उन्हें 3 या 4 नई श्रेणियों को संक्षिप्त करना चाहता हूं। सबसे अच्छा तरीका क्या है?

यदि मैं SQL जैसे उपकरण में था, तो मैं एक CASE कथन का उपयोग करूंगा लेकिन यह सुनिश्चित नहीं करूंगा कि R में यह हमला कैसे किया जाए।

किसी भी मदद आप प्रदान कर सकते हैं बहुत सराहना की जाएगी!


a) क्या वे पूर्णांक, संख्यात्मक, श्रेणीबद्ध या स्ट्रिंग हैं? कृपया उदाहरण डेटा स्निपेट पोस्ट करें, dput()b का उपयोग करते हुए ) क्या आप बेस R, dplyr, data.table, tidyaderse में एक समाधान चाहते हैं ...?
smci

जवाबों:


38

case_when(), जो मई 2016 में 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
आपको .$प्रत्येक स्तंभ के सामने की आवश्यकता नहीं है ।
काठ

1
हां, dplyr 0.7.0 (9 जून, 2017 को जारी) के रूप में, .$अब आवश्यक नहीं है। जिस समय यह उत्तर मूल रूप से लिखा गया था, वह था।
इवान कोर्टेंस

महान समाधान। यदि दोनों कथन सत्य हैं। क्या दूसरा वाला पहला ओवरराइट कर रहा है?
जेडपी

1
@JdP यह SQL में CASE WHEN की तरह ही काम करता है, इसलिए बयानों का मूल्यांकन क्रम में किया जाता है, और परिणाम पहले TRZ स्टेटमेंट है। (इसलिए ऊपर के उदाहरण में, मैंने अंत में एक TRUE में रखा है, जो डिफ़ॉल्ट मान के रूप में कार्य करता है।)
इवान कॉर्टेंस

मुझे यह उत्तर पसंद है क्योंकि, इसके विपरीत switch, यह आपको मामलों के लिए कुंजियों के बजाय अभिव्यक्ति का एक क्रम बनाने की अनुमति देता है।
Dannid

27

पैकेज casesसे फ़ंक्शन पर एक नज़र डालें memisc। यह केस-कार्यक्षमता को दो अलग-अलग तरीकों से उपयोग करने के लिए लागू करता है। पैकेज में उदाहरणों से:

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

कहाँ xऔर yदो वैक्टर हैं।

संदर्भ: मेमिस पैकेज , मामलों का उदाहरण


23

यदि आपको मिला 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"))

1
अच्छा जवाब। मैं भूल गया कि आप एक सूची का उपयोग पुराने और नए नामों के स्तर के तर्क के रूप में कर सकते हैं; मेरा समाधान स्तरों के क्रम को सीधा रखने पर निर्भर करता है, इसलिए यह उस तरह से बेहतर है।
हारून ने

साथ ही, xअंतिम पंक्ति में होना चाहिए changelevels?
हारून ने

20

यहाँ 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प्रत्येक आइटम के लिए श्रेणी का नाम ( , आदि) लिखना होगा । नीचे के रूप में हमारी श्रेणियों को परिभाषित करने में सक्षम होने के लिए यह बहुत सुविधाजनक है (देखें इसी तरह का प्रश्न आर में डेटा फ्रेम में एक कॉलम कैसे जोड़ते हैं )

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

और हम किसी तरह इस मैपिंग को "उल्टा" करना चाहते हैं। मैं अपना स्वयं का इनवॉइस फ़ंक्शन लिखता हूं:

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

16

मुझे 'स्विच' के लिए कोई प्रस्ताव नहीं मिला। कोड उदाहरण (इसे चलाएँ):

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

14

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.Fung

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" ...

मुझे बाद में पता चला कि वास्तव में दो अलग-अलग स्विच फ़ंक्शन हैं। यह सामान्य समारोह नहीं है, लेकिन आप इसके बारे में के रूप में या तो सोचना चाहिए switch.numericया switch.character। यदि आपका पहला तर्क एक आर 'कारक' है, तो आप 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
मैं सिर्फ एक फ़ंक्शन का समर्थन नहीं कर सकता, जो पाठ से पैरामीटर है
हैडली

हां, लेकिन क्या आप जानते हैं कि किसी ने एक बेहतर संस्करण लिखा है? sos::findFn("recode")पाता doBy::recodeVar, epicalc::recode, memisc::recode, लेकिन मैं उन पर विस्तार से देखा नहीं है ...
बेन Bolker

5

मुझे इनमें से कोई पसंद नहीं है, वे पाठक या संभावित उपयोगकर्ता के लिए स्पष्ट नहीं हैं। मैं सिर्फ एक अनाम फ़ंक्शन का उपयोग करता हूं, वाक्यविन्यास एक केस स्टेटमेंट के रूप में चालाक नहीं है, लेकिन मूल्यांकन एक केस स्टेटमेंट के समान है और दर्दनाक नहीं है। यह आपके मूल्यांकन का आकलन भी करता है कि आपके चरों को कहां परिभाषित किया गया है।

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) यह केवल काम करता है अगर xऔर yस्केलर हैं; वैक्टर के लिए, मूल प्रश्न के रूप में, नेस्टेड ifelseबयान आवश्यक होंगे।
हारून ने

4

मैं उन मामलों में उपयोग कर रहा हूं जिनका आप उल्लेख कर रहे हैं switch()। यह एक नियंत्रण कथन जैसा दिखता है लेकिन वास्तव में, यह एक फ़ंक्शन है। अभिव्यक्ति का मूल्यांकन किया जाता है और इस मूल्य के आधार पर, सूची में संबंधित आइटम वापस आ जाता है।

स्विच दो अलग-अलग तरीकों से काम करता है, यह निर्भर करता है कि पहला तर्क किसी वर्ण स्ट्रिंग या संख्या का मूल्यांकन करता है या नहीं।

निम्न प्रकार एक सरल स्ट्रिंग उदाहरण है जो आपकी समस्या को पुरानी श्रेणियों को नए लोगों तक समेटने के लिए हल करता है।

वर्ण-स्ट्रिंग प्रपत्र के लिए, नामांकित मानों के बाद डिफ़ॉल्ट के रूप में एक एकल अनाम तर्क है।

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

3

यदि आप sql की तरह सिंटेक्स रखना चाहते हैं तो आप सिर्फ sqldfपैकेज का उपयोग कर सकते हैं । Tthe फ़ंक्शन का उपयोग किया जाना भी नाम है sqldfऔर सिंटैक्स निम्नानुसार है

sqldf(<your query in quotation marks>)

2

एक केस स्टेटमेंट वास्तव में यहाँ सही दृष्टिकोण नहीं हो सकता है। यदि यह एक कारक है, जो कि संभावना है, तो बस कारक के स्तर को उचित रूप से निर्धारित करें।

मान लीजिए कि आपके पास 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

बी और सी के स्तर में शामिल होने और इसे बीसी नाम देने के लिए, बस उन स्तरों के नामों को बीसी में बदल दें।

> 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::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]

बोनस अंक अगर कॉलम चार के बजाय एक कारक के रूप में म्यूट से बाहर आ सकता है! 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()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') ]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.