শর্তসাপেক্ষ পরিবর্তনের জন্য dplyr প্যাকেজ ব্যবহার করা যেতে পারে?


178

পরিবর্তনটি শর্তযুক্ত হলে (কিছু কলাম মানগুলির মানের উপর নির্ভরশীল) কী মিউটেট ব্যবহার করা যেতে পারে?

এই উদাহরণটি আমার অর্থ বোঝাতে সহায়তা করে।

structure(list(a = c(1, 3, 4, 6, 3, 2, 5, 1), b = c(1, 3, 4, 
2, 6, 7, 2, 6), c = c(6, 3, 6, 5, 3, 6, 5, 3), d = c(6, 2, 4, 
5, 3, 7, 2, 6), e = c(1, 2, 4, 5, 6, 7, 6, 3), f = c(2, 3, 4, 
2, 2, 7, 5, 2)), .Names = c("a", "b", "c", "d", "e", "f"), row.names = c(NA, 
8L), class = "data.frame")

  a b c d e f
1 1 1 6 6 1 2
2 3 3 3 2 2 3
3 4 4 6 4 4 4
4 6 2 5 5 5 2
5 3 6 3 3 6 2
6 2 7 6 7 7 7
7 5 2 5 2 6 5
8 1 6 3 6 3 2

আমি dplyr প্যাকেজটি ব্যবহার করে আমার সমস্যার সমাধানের জন্য প্রত্যাশা করছিলাম (এবং হ্যাঁ, আমি জানি এটি কার্যকর নয় এমন কোড নয়, তবে আমি অনুমান করি যে এটি উদ্দেশ্যটি পরিষ্কার করে দেয়) একটি নতুন কলাম তৈরি করার জন্য:

 library(dplyr)
 df <- mutate(df,
         if (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)){g = 2},
         if (a == 0 | a == 1 | a == 4 | a == 3 |  c == 4) {g = 3})

আমি যে কোডটির সন্ধান করছি তার এই বিশেষ উদাহরণে এই ফলাফল হওয়া উচিত:

  a b c d e f  g
1 1 1 6 6 1 2  3
2 3 3 3 2 2 3  3
3 4 4 6 4 4 4  3
4 6 2 5 5 5 2 NA
5 3 6 3 3 6 2 NA
6 2 7 6 7 7 7  2
7 5 2 5 2 6 5  2
8 1 6 3 6 3 2  3

কীভাবে dplyr এ এটি করবেন সে সম্পর্কে কারও কি ধারণা আছে? এই ডেটা ফ্রেমটি একটি উদাহরণ, আমি যে ডেটা ফ্রেমগুলি নিয়ে কাজ করছি তা অনেক বড়। এর গতির কারণে আমি dplyr ব্যবহার করার চেষ্টা করেছি, তবে সম্ভবত এই সমস্যাটি পরিচালনা করার জন্য আরও ভাল, আরও ভাল উপায় আছে?


2
হ্যাঁ কিন্তু dplyr::case_when()একটি তুলনায় অনেক পরিষ্কার হয় ifelse,
smci

উত্তর:


216

ব্যবহার ifelse

df %>%
  mutate(g = ifelse(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4), 2,
               ifelse(a == 0 | a == 1 | a == 4 | a == 3 |  c == 4, 3, NA)))

যুক্ত - if_else: দ্রষ্টব্য যে dplyr 0.5 তে একটি if_elseফাংশন সংজ্ঞায়িত করা হয়েছে যাতে বিকল্পের ifelseসাথে প্রতিস্থাপন করা হবে if_else; তবে নোট করুন যেহেতু আরও if_elseকঠোর ifelse(শর্তের উভয় পায়ে একই ধরণের থাকতে হবে) সুতরাং সেই NAক্ষেত্রে এটি প্রতিস্থাপন করতে হবে NA_real_

df %>%
  mutate(g = if_else(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4), 2,
               if_else(a == 0 | a == 1 | a == 4 | a == 3 |  c == 4, 3, NA_real_)))

যুক্ত - কেস_হেতু এই প্রশ্নটি যেহেতু পোস্ট করা হয়েছিল dplyr যুক্ত করেছে case_whenতাই অন্য বিকল্প হ'ল :

df %>% mutate(g = case_when(a == 2 | a == 5 | a == 7 | (a == 1 & b == 4) ~ 2,
                            a == 0 | a == 1 | a == 4 | a == 3 |  c == 4 ~ 3,
                            TRUE ~ NA_real_))

যোগ করা হয়েছে - গাণিতিক / na_if যদি মানগুলি সংখ্যাসূচক হয় এবং শর্তগুলি (শেষে এনএর ডিফল্ট মান ব্যতীত) পারস্পরিক একচেটিয়া হয়, তবে প্রশ্নের ক্ষেত্রে যেমন রয়েছে, তখন আমরা একটি গাণিতিক ভাবটি ব্যবহার করতে পারি যেমন প্রতিটি শব্দ গুণিত হয় na_ifশেষে এনএ দিয়ে 0 প্রতিস্থাপন ব্যবহার করে কাঙ্ক্ষিত ফলাফল দ্বারা ।

df %>%
  mutate(g = 2 * (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)) +
             3 * (a == 0 | a == 1 | a == 4 | a == 3 |  c == 4),
         g = na_if(g, 0))

3
এর পরিবর্তে যুক্তি কী? NAআমি শর্তগুলি পূরণ করে না এমন সারিগুলি কেবল একই থাকতে চাই?
নাজির

10
mutate(g = ifelse(condition1, 2, ifelse(condition2, 3, g))
জি। গ্রোথেন্ডিক

11
কেস_সুও খুব সুন্দর, এবং এটি আসলে সেখানে ছিল তা বুঝতে আমার খুব বেশি সময় লেগেছে। আমি মনে করি এটি সাধারণ dplyr টিউটোরিয়ালের মধ্যে হওয়া উচিত, ডেটা সাবসেটগুলির জন্য স্টাফ গণনা করার প্রয়োজনীয়তা থাকা খুব সাধারণ বিষয়, তবে এখনও ডেটা সম্পূর্ণ রাখতে চাইছেন।
জাভিয়ের ফাজার্দো

55

যেহেতু আপনি সমস্যাটি পরিচালনা করার জন্য আরও ভাল উপায়ের জন্য জিজ্ঞাসা করছেন তাই এখানে ব্যবহারের অন্য উপায় রয়েছে data.table:

require(data.table) ## 1.9.2+
setDT(df)
df[a %in% c(0,1,3,4) | c == 4, g := 3L]
df[a %in% c(2,5,7) | (a==1 & b==4), g := 2L]

নোট করুন শর্তাধীন বিবৃতিগুলির ক্রমটি gসঠিকভাবে পাওয়ার জন্য বিপরীত । gতৈরি করার কোনও অনুলিপি নেই , এমনকি দ্বিতীয় কার্যভারের সময়ও - এটি স্থানটিতে স্থান পেয়েছে

বৃহত্তর ডেটাতে এটি নেস্টেড ব্যবহারের চেয়ে ভাল পারফরম্যান্স হতে পারে if-else, কারণ এটি 'হ্যাঁ' এবং 'না' উভয় ক্ষেত্রেই মূল্যায়ন করতে পারে এবং আইএমএইচও পড়তে / বজায় রাখতে বাসা বাঁধাই আরও কঠিন হতে পারে।


অপেক্ষাকৃত বড় ডেটার জন্য এখানে একটি মানদণ্ড রয়েছে:

# R version 3.1.0
require(data.table) ## 1.9.2
require(dplyr)
DT <- setDT(lapply(1:6, function(x) sample(7, 1e7, TRUE)))
setnames(DT, letters[1:6])
# > dim(DT) 
# [1] 10000000        6
DF <- as.data.frame(DT)

DT_fun <- function(DT) {
    DT[(a %in% c(0,1,3,4) | c == 4), g := 3L]
    DT[a %in% c(2,5,7) | (a==1 & b==4), g := 2L]
}

DPLYR_fun <- function(DF) {
    mutate(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, 
            ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_)))
}

BASE_fun <- function(DF) { # R v3.1.0
    transform(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, 
            ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_)))
}

system.time(ans1 <- DT_fun(DT))
#   user  system elapsed 
#  2.659   0.420   3.107 

system.time(ans2 <- DPLYR_fun(DF))
#   user  system elapsed 
# 11.822   1.075  12.976 

system.time(ans3 <- BASE_fun(DF))
#   user  system elapsed 
# 11.676   1.530  13.319 

identical(as.data.frame(ans1), as.data.frame(ans2))
# [1] TRUE

identical(as.data.frame(ans1), as.data.frame(ans3))
# [1] TRUE

আপনি যে বিকল্পটি চেয়েছিলেন এটি কোনও বিকল্প কিনা তা নিশ্চিত নই, তবে আমি আশা করি এটি সহায়তা করবে।


4
কোডের চমৎকার টুকরো! জি। গ্রোটেন্ডিকের উত্তরটি কাজ করে এবং সংক্ষিপ্ত তাই আমি আমার প্রশ্নের উত্তর হিসাবে সেটিকে বেছে নিয়েছি, তবে আপনার সমাধানের জন্য আপনাকে ধন্যবাদ জানাই। আমি নিশ্চিত এটি এটি এইভাবে চেষ্টা করবে।
rdatasculptor

যেহেতু DT_funএটির ইনপুটটি স্থান পরিবর্তন করছে, তাই বেঞ্চমার্কটি বেশ ন্যায্য হবে না - ২ য় পুনরাবৃত্তি ফরোয়ার্ড থেকে একই ইনপুট না পাওয়ার পাশাপাশি (যা DT$gইতিমধ্যে বরাদ্দ হওয়ার সময়কে প্রভাবিত করতে পারে ?), ফলাফলটি আবারও প্রচার করে ans1এবং তাই সম্ভবত ( যদি আর এর অপ্টিমাইজারটি এটি প্রয়োজনীয় মনে করে? তবে এটির উপর নিশ্চিত নয় ...) অন্য একটি অনুলিপি এড়ানো উচিত DPLYR_funএবং এটি করা BASE_funদরকার?
কেন উইলিয়ামস

যদিও স্পষ্ট করে বলতে গেলে , আমি মনে করি যে এই data.tableসমাধানটি দুর্দান্ত and data.tableএটি জায়গায় পরিবর্তনের বিষয়ে সত্যই সতর্ক হওয়া প্রয়োজন, যদিও!
কেন উইলিয়ামস

আমি ডেটা.ট্যাবল থেকে আরও পরিপাটি জিনিসগুলিতে অভ্যস্ত হওয়ার চেষ্টা করছি এবং এটি একটি সাধারণ সাধারণ ব্যবহারের উদাহরণগুলির মধ্যে একটি যা ডেটা.ট্যাবল সহজেই পড়া সহজ এবং আরও দক্ষ। আমার শব্দভাণ্ডারে আরও পরিপাটি বিকাশের জন্য প্রধান কারণটি আমার নিজের এবং অন্যদের জন্য পঠনযোগ্যতা, তবে এক্ষেত্রে এটি ডেটা টেটেবল জয়ের মতো বলে মনে হয়।
পল ম্যাকমার্ডি

38

dplyr এর এখন একটি ফাংশন রয়েছে case_whenযা ভেক্টরাইজড অফার দেয় যদি। সিনট্যাক্স তুলনায় একটু অদ্ভুত mosaic:::derivedFactorআপনি এন মোড ঘোষণা পারবেন না মান dplyr ভাবে এক্সেস ভেরিয়েবল, এবং প্রয়োজনে, কিন্তু এটা যথেষ্ট দ্রুত চেয়ে mosaic:::derivedFactor

df %>%
mutate(g = case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, 
                     a %in% c(0,1,3,4) | c == 4 ~ 3L, 
                     TRUE~as.integer(NA)))

সম্পাদনা: আপনি যদি dplyr::case_when()প্যাকেজের ০.7.০ সংস্করণটি ব্যবহার করে থাকেন তবে আপনাকে ' .$' (উদাহরণস্বরূপ .$a == 1ভিতরে লিখুন case_when) দিয়ে ভেরিয়েবলের আগে থাকা প্রয়োজন ।

বেঞ্চমার্ক : মাপদণ্ডের জন্য (অরুণের পোস্ট থেকে ফাংশন পুনরায় ব্যবহার করা) এবং নমুনার আকার হ্রাস:

require(data.table) 
require(mosaic) 
require(dplyr)
require(microbenchmark)

set.seed(42) # To recreate the dataframe
DT <- setDT(lapply(1:6, function(x) sample(7, 10000, TRUE)))
setnames(DT, letters[1:6])
DF <- as.data.frame(DT)

DPLYR_case_when <- function(DF) {
  DF %>%
  mutate(g = case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, 
                       a %in% c(0,1,3,4) | c==4 ~ 3L, 
                       TRUE~as.integer(NA)))
}

DT_fun <- function(DT) {
  DT[(a %in% c(0,1,3,4) | c == 4), g := 3L]
  DT[a %in% c(2,5,7) | (a==1 & b==4), g := 2L]
}

DPLYR_fun <- function(DF) {
  mutate(DF, g = ifelse(a %in% c(2,5,7) | (a==1 & b==4), 2L, 
                    ifelse(a %in% c(0,1,3,4) | c==4, 3L, NA_integer_)))
}

mosa_fun <- function(DF) {
  mutate(DF, g = derivedFactor(
    "2" = (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)),
    "3" = (a == 0 | a == 1 | a == 4 | a == 3 |  c == 4),
    .method = "first",
    .default = NA
  ))
}

perf_results <- microbenchmark(
  dt_fun <- DT_fun(copy(DT)),
  dplyr_ifelse <- DPLYR_fun(copy(DF)),
  dplyr_case_when <- DPLYR_case_when(copy(DF)),
  mosa <- mosa_fun(copy(DF)),
  times = 100L
)

এটি দেয়:

print(perf_results)
Unit: milliseconds
           expr        min         lq       mean     median         uq        max neval
         dt_fun   1.391402    1.560751   1.658337   1.651201   1.716851   2.383801   100
   dplyr_ifelse   1.172601    1.230351   1.331538   1.294851   1.390351   1.995701   100
dplyr_case_when   1.648201    1.768002   1.860968   1.844101   1.958801   2.207001   100
           mosa 255.591301  281.158350 291.391586 286.549802 292.101601 545.880702   100

case_whenএছাড়াও এই হিসাবে লেখা যেতে পারে:df %>% mutate(g = with(., case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, a %in% c(0,1,3,4) | c==4 ~ 3L, TRUE ~ NA_integer_)))
জি। গ্রোথেনডিক

3
মাইক্রোসেকেন্ড / মিলিসেকেন্ড / দিনগুলিতে এই মানদণ্ডটি কী? এই মানদণ্ডটি পরিমাপ ইউনিট সরবরাহ না করে অর্থহীন। এছাড়াও, 1e6 এর চেয়ে ছোট ডেটা সেট করে বেঞ্চ-মার্কিং অর্থহীন কারণ এটি স্কেল করে না।
ডেভিড আরেনবুর্গ

3
.$প্লিজ আপনার উত্তরটি পরিবর্তন করুন, ডিসপ্লায়ারের নতুন সংস্করণে আপনার আর দরকার নেই
অমিত কোহলি

14

derivedFactorথেকে ফাংশন mosaicপ্যাকেজ এই হ্যান্ডেল করার জন্য ডিজাইন করা বলে মনে হয়। এই উদাহরণটি ব্যবহার করে, এটি দেখতে দেখতে এমন হবে:

library(dplyr)
library(mosaic)
df <- mutate(df, g = derivedFactor(
     "2" = (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)),
     "3" = (a == 0 | a == 1 | a == 4 | a == 3 |  c == 4),
     .method = "first",
     .default = NA
     ))

(আপনি যদি ফলাফলটি কোনও ফ্যাক্টরের পরিবর্তে সংখ্যাসূচক হতে চান তবে derivedFactorআপনি একটি as.numericকলটি মোড়ানো করতে পারেন ))

derivedFactor শর্তসাপেক্ষে একটি স্বেচ্ছাসেবী সংখ্যার জন্যও ব্যবহার করা যেতে পারে।


4
@ হডলিকে এটিকে ডিপি্লায়ারের জন্য ডিফল্ট বাক্য গঠন করতে হবে। নেস্টেড "ifelse" বিবৃতি প্রয়োজন প্যাকেজের একক নিকৃষ্ট অংশ, যা প্রধানত ক্ষেত্রে কারণ অন্যান্য ফাংশন খুব ভাল
roreoren

.asFactor = Fবিকল্পটি ব্যবহার করে বা derivedVariableএকই প্যাকেজে (অনুরূপ) ফাংশনটি ব্যবহার করে আপনি ফলাফলকে ফ্যাক্টর হতে বাধা দিতে পারেন ।
জ্যাক ফিশার

দেখে মনে হচ্ছে recodedplyr 0.5 থেকে এটি করবে। যদিও আমি এখনও এটি তদন্ত করি নি। দেখুন blog.rstudio.org/2016/06/27/dplyr-0-5-0
জ্যাক ফিশার

12

case_when এসকিউএল-স্টাইলের কেসটির এখন একটি পরিষ্কার পরিচ্ছন্ন বাস্তবায়ন যখন:

structure(list(a = c(1, 3, 4, 6, 3, 2, 5, 1), b = c(1, 3, 4, 
2, 6, 7, 2, 6), c = c(6, 3, 6, 5, 3, 6, 5, 3), d = c(6, 2, 4, 
5, 3, 7, 2, 6), e = c(1, 2, 4, 5, 6, 7, 6, 3), f = c(2, 3, 4, 
2, 2, 7, 5, 2)), .Names = c("a", "b", "c", "d", "e", "f"), row.names = c(NA, 
8L), class = "data.frame") -> df


df %>% 
    mutate( g = case_when(
                a == 2 | a == 5 | a == 7 | (a == 1 & b == 4 )     ~   2,
                a == 0 | a == 1 | a == 4 |  a == 3 | c == 4       ~   3
))

Dplyr 0.7.4 ব্যবহার করে

ম্যানুয়াল: http://dplyr.tidyverse.org/references/ کیس_when.html

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.