dplyr กลายพันธุ์ / แทนที่หลายคอลัมน์ในชุดย่อยของแถว


86

ฉันอยู่ระหว่างการลองใช้เวิร์กโฟลว์ที่ใช้ dplyr (แทนที่จะใช้ data.table ส่วนใหญ่ซึ่งฉันเคยชิน) และฉันเจอปัญหาที่ฉันไม่สามารถหาโซลูชัน dplyr ที่เทียบเท่าได้ . ฉันมักจะพบกับสถานการณ์ที่ฉันต้องอัปเดต / แทนที่หลายคอลัมน์ตามเงื่อนไขตามเงื่อนไขเดียว นี่คือตัวอย่างโค้ดพร้อมโซลูชัน data.table ของฉัน:

library(data.table)

# Create some sample data
set.seed(1)
dt <- data.table(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                               replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50))

# Replace the values of several columns for rows where measure is "exit"
dt <- dt[measure == 'exit', 
         `:=`(qty.exit = qty,
              cf = 0,
              delta.watts = 13)]

มีวิธีแก้ไข dplyr ง่ายๆสำหรับปัญหาเดียวกันนี้หรือไม่? ฉันต้องการหลีกเลี่ยงการใช้ ifelse เพราะฉันไม่ต้องการที่จะต้องพิมพ์เงื่อนไขหลาย ๆ ครั้ง - นี่เป็นตัวอย่างที่เรียบง่าย แต่บางครั้งก็มีการกำหนดจำนวนมากตามเงื่อนไขเดียว

ขอบคุณล่วงหน้าสำหรับความช่วยเหลือ!

คำตอบ:


83

โซลูชันเหล่านี้ (1) รักษาไปป์ไลน์ (2) ไม่เขียนทับอินพุตและ (3) กำหนดให้ระบุเงื่อนไขเพียงครั้งเดียวเท่านั้น:

1a) mutate_condสร้างฟังก์ชันง่ายๆสำหรับ data frames หรือตารางข้อมูลที่สามารถรวมเข้ากับ pipeline ฟังก์ชั่นนี้เหมือนmutateแต่ทำหน้าที่เฉพาะกับแถวที่ตรงตามเงื่อนไข:

mutate_cond <- function(.data, condition, ..., envir = parent.frame()) {
  condition <- eval(substitute(condition), .data, envir)
  .data[condition, ] <- .data[condition, ] %>% mutate(...)
  .data
}

DF %>% mutate_cond(measure == 'exit', qty.exit = qty, cf = 0, delta.watts = 13)

1b) mutate_lastนี่เป็นฟังก์ชันทางเลือกสำหรับ data frames หรือตารางข้อมูลซึ่งเหมือนอีกครั้งmutateแต่ใช้เฉพาะภายในgroup_by(ตามตัวอย่างด้านล่าง) และทำงานเฉพาะในกลุ่มสุดท้ายแทนที่จะเป็นทุกกลุ่ม โปรดสังเกตว่า TRUE> FALSE ดังนั้นหากgroup_byระบุเงื่อนไขก็mutate_lastจะดำเนินการกับแถวที่ตรงตามเงื่อนไขนั้นเท่านั้น

mutate_last <- function(.data, ...) {
  n <- n_groups(.data)
  indices <- attr(.data, "indices")[[n]] + 1
  .data[indices, ] <- .data[indices, ] %>% mutate(...)
  .data
}


DF %>% 
   group_by(is.exit = measure == 'exit') %>%
   mutate_last(qty.exit = qty, cf = 0, delta.watts = 13) %>%
   ungroup() %>%
   select(-is.exit)

2) แยกเงื่อนไขแยกตัวประกอบออกจากเงื่อนไขโดยทำให้เป็นคอลัมน์พิเศษซึ่งจะถูกลบออกในภายหลัง จากนั้นใช้ifelse, replaceหรือทางคณิตศาสตร์กับ logicals ดังแสดงในภาพ นอกจากนี้ยังใช้ได้กับตารางข้อมูล

library(dplyr)

DF %>% mutate(is.exit = measure == 'exit',
              qty.exit = ifelse(is.exit, qty, qty.exit),
              cf = (!is.exit) * cf,
              delta.watts = replace(delta.watts, is.exit, 13)) %>%
       select(-is.exit)

3) sqldfเราสามารถใช้ SQL updateผ่านแพ็คเกจ sqldf ในไปป์ไลน์สำหรับ data frames (แต่ไม่ใช่ตารางข้อมูลเว้นแต่เราจะแปลง - นี่อาจแสดงถึงจุดบกพร่องใน dplyr ดูdplyr ฉบับ 1579 ) อาจดูเหมือนว่าเรากำลังปรับเปลี่ยนอินพุตในโค้ดนี้อย่างไม่น่าปรารถนาเนื่องจากการมีอยู่ของupdateแต่ในความเป็นจริงupdateกำลังทำหน้าที่คัดลอกข้อมูลเข้าในฐานข้อมูลที่สร้างขึ้นชั่วคราวไม่ใช่กับอินพุตจริง

library(sqldf)

DF %>% 
   do(sqldf(c("update '.' 
                 set 'qty.exit' = qty, cf = 0, 'delta.watts' = 13 
                 where measure = 'exit'", 
              "select * from '.'")))

4) row_case_whenยังตรวจสอบที่row_case_whenกำหนดไว้ใน Returning a tibble: How to vectorize with case_when? . ใช้ไวยากรณ์คล้ายกับcase_whenแต่ใช้กับแถว

library(dplyr)

DF %>%
  row_case_when(
    measure == "exit" ~ data.frame(qty.exit = qty, cf = 0, delta.watts = 13),
    TRUE ~ data.frame(qty.exit, cf, delta.watts)
  )

หมายเหตุ 1:เราใช้สิ่งนี้เป็นDF

set.seed(1)
DF <- data.frame(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                               replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50))

หมายเหตุ 2:ปัญหาเกี่ยวกับวิธีระบุการอัปเดตชุดย่อยของแถวอย่างง่ายดายยังมีการกล่าวถึงในปัญหา dplyr 134 , 631 , 1518และ1573โดยมี631เป็นเธรดหลักและ1573เป็นการตรวจสอบคำตอบที่นี่


1
คำตอบที่ยอดเยี่ยมขอบคุณ! mutate_cond และ mutate_ ของคุณ @Kevin Ushey เมื่อทั้งคู่เป็นวิธีแก้ปัญหาที่ดีสำหรับปัญหานี้ ฉันคิดว่าฉันชอบเล็กน้อยสำหรับความสามารถในการอ่าน / ความยืดหยุ่นของ mutate_ เมื่อนั้น แต่ฉันจะให้คำตอบนี้ว่า "ตรวจสอบ" เพื่อความละเอียดถี่ถ้วน
Chris Newton

ฉันชอบวิธี mutate_cond มาก ดูเหมือนว่าฉันจะชอบฟังก์ชั่นนี้หรือสิ่งที่ใกล้เคียงกันมากซึ่งรวมอยู่ใน dplyr และจะเป็นทางออกที่ดีกว่า VectorizedSwitch (ที่กล่าวถึงในgithub.com/hadley/dplyr/issues/1573 ) สำหรับกรณีการใช้งานที่ผู้คนกำลังคิด เกี่ยวกับที่นี่ ...
Magnus

ฉันรัก mutate_cond ตัวเลือกต่างๆควรเป็นคำตอบแยกกัน
Holger Brandl

เป็นเวลาสองสามปีแล้วที่ปัญหา Github ดูเหมือนปิดและถูกล็อก มีวิธีแก้ปัญหานี้อย่างเป็นทางการหรือไม่?
static_rtti

27

คุณสามารถทำได้โดยmagrittrใช้ท่อสองทาง%<>%:

library(dplyr)
library(magrittr)

dt[dt$measure=="exit",] %<>% mutate(qty.exit = qty,
                                    cf = 0,  
                                    delta.watts = 13)

ซึ่งจะช่วยลดปริมาณการพิมพ์ แต่ยังช้ากว่าdata.tableมาก


อันที่จริงตอนนี้ฉันมีโอกาสทดสอบสิ่งนี้แล้วฉันต้องการวิธีแก้ปัญหาที่หลีกเลี่ยงความจำเป็นในการตั้งค่าย่อยโดยใช้สัญกรณ์ dt [dt $ measure == 'exit',] เนื่องจากอาจไม่สามารถใช้งานได้นานขึ้น ชื่อ dt
Chris Newton

เป็นเพียง FYI แต่โซลูชันนี้จะใช้ได้เฉพาะเมื่อdata.frame/ tibbleมีคอลัมน์ที่กำหนดโดยmutate. จะใช้ไม่ได้ถ้าคุณพยายามเพิ่มคอลัมน์ใหม่เช่นครั้งแรกที่ทำงานผ่านลูปและแก้ไขไฟล์data.frame.
Ursus Frost

@UrsusFrost การเพิ่มคอลัมน์ใหม่ที่เป็นเพียงส่วนย่อยของชุดข้อมูลดูเหมือนจะแปลกสำหรับฉัน คุณเพิ่ม NA ในแถวที่ไม่ได้ย่อย?
Baraliuh

@Baraliuh ใช่ฉันรู้สึกขอบคุณ เป็นส่วนหนึ่งของลูปที่ฉันเพิ่มและต่อท้ายข้อมูลในรายการวันที่ วันที่สองสามวันแรกจะต้องได้รับการปฏิบัติที่แตกต่างจากวันที่ตามมาเนื่องจากเป็นการจำลองกระบวนการทางธุรกิจในโลกแห่งความเป็นจริง ในการทำซ้ำเพิ่มเติมขึ้นอยู่กับเงื่อนไขของวันที่ข้อมูลจะคำนวณแตกต่างกัน เนื่องจากเงื่อนไขฉันไม่ต้องการเปลี่ยนวันที่ก่อนหน้าโดยไม่ได้ตั้งใจในไฟล์data.frame. FWIW ฉันเพิ่งกลับไปใช้data.tableแทนdplyrเพราะiนิพจน์ของมันจัดการสิ่งนี้ได้อย่างง่ายดายบวกกับลูปโดยรวมทำงานได้เร็วขึ้นมาก
Ursus Frost

19

นี่เป็นวิธีแก้ปัญหาที่ฉันชอบ:

mutate_when <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  for (i in seq(1, length(dots), by = 2)) {
    condition <- eval(dots[[i]], envir = data)
    mutations <- eval(dots[[i + 1]], envir = data[condition, , drop = FALSE])
    data[condition, names(mutations)] <- mutations
  }
  data
}

ช่วยให้คุณเขียนสิ่งต่างๆเช่นเช่น

mtcars %>% mutate_when(
  mpg > 22,    list(cyl = 100),
  disp == 160, list(cyl = 200)
)

ซึ่งค่อนข้างอ่านได้ - แม้ว่าอาจจะไม่ได้มีประสิทธิภาพเท่าที่ควร


14

ดังที่ eipi10 แสดงไว้ข้างต้นไม่มีวิธีง่ายๆในการแทนที่ชุดย่อยใน dplyr เนื่องจาก DT ใช้ความหมายแบบ pass-by-reference เทียบกับ dplyr โดยใช้ pass-by-value dplyr ต้องการการใช้ifelse()บนเวกเตอร์ทั้งหมดในขณะที่ DT จะทำการเซตย่อยและอัปเดตโดยการอ้างอิง (ส่งคืน DT ทั้งหมด) ดังนั้นสำหรับแบบฝึกหัดนี้ DT จะเร็วขึ้นมาก

คุณสามารถเลือกย่อยก่อนจากนั้นอัปเดตและสุดท้ายรวมกันใหม่:

dt.sub <- dt[dt$measure == "exit",] %>%
  mutate(qty.exit= qty, cf= 0, delta.watts= 13)

dt.new <- rbind(dt.sub, dt[dt$measure != "exit",])

แต่ DT จะเร็วขึ้นมาก: (แก้ไขให้ใช้คำตอบใหม่ของ eipi10)

library(data.table)
library(dplyr)
library(microbenchmark)
microbenchmark(dt= {dt <- dt[measure == 'exit', 
                            `:=`(qty.exit = qty,
                                 cf = 0,
                                 delta.watts = 13)]},
               eipi10= {dt[dt$measure=="exit",] %<>% mutate(qty.exit = qty,
                                cf = 0,  
                                delta.watts = 13)},
               alex= {dt.sub <- dt[dt$measure == "exit",] %>%
                 mutate(qty.exit= qty, cf= 0, delta.watts= 13)

               dt.new <- rbind(dt.sub, dt[dt$measure != "exit",])})


Unit: microseconds
expr      min        lq      mean   median       uq      max neval cld
     dt  591.480  672.2565  747.0771  743.341  780.973 1837.539   100  a 
 eipi10 3481.212 3677.1685 4008.0314 3796.909 3936.796 6857.509   100   b
   alex 3412.029 3637.6350 3867.0649 3726.204 3936.985 5424.427   100   b

10

ฉันเพิ่งเจอสิ่งนี้และชอบmutate_cond()โดย @G Grothendieck แต่คิดว่าอาจมีประโยชน์ในการจัดการกับตัวแปรใหม่ ๆ ด้านล่างมีสองส่วนเพิ่มเติม:

ไม่เกี่ยวข้อง: บรรทัดสุดท้ายที่สองเพิ่มขึ้นอีกเล็กน้อยdplyrโดยใช้filter()

บรรทัดใหม่สามบรรทัดที่จุดเริ่มต้นรับชื่อตัวแปรสำหรับใช้mutate()และเริ่มต้นตัวแปรใหม่ใน data frame ก่อนที่จะmutate()เกิด ตัวแปรใหม่จะเริ่มต้นสำหรับส่วนที่เหลือของการdata.frameใช้new_initงานซึ่งตั้งค่าเป็น missing ( NA) เป็นค่าเริ่มต้น

mutate_cond <- function(.data, condition, ..., new_init = NA, envir = parent.frame()) {
  # Initialize any new variables as new_init
  new_vars <- substitute(list(...))[-1]
  new_vars %<>% sapply(deparse) %>% names %>% setdiff(names(.data))
  .data[, new_vars] <- new_init

  condition <- eval(substitute(condition), .data, envir)
  .data[condition, ] <- .data %>% filter(condition) %>% mutate(...)
  .data
}

นี่คือตัวอย่างบางส่วนโดยใช้ข้อมูลม่านตา:

เปลี่ยนPetal.Lengthถึง 88 Species == "setosa"ที่ สิ่งนี้จะทำงานในฟังก์ชันเดิมเช่นเดียวกับเวอร์ชันใหม่นี้

iris %>% mutate_cond(Species == "setosa", Petal.Length = 88)

เหมือนกับข้างบน แต่ยังสร้างตัวแปรใหม่x( NAในแถวที่ไม่รวมอยู่ในเงื่อนไข) ไม่ได้มาก่อน.

iris %>% mutate_cond(Species == "setosa", Petal.Length = 88, x = TRUE)

เหมือนกับด้านบน แต่แถวที่ไม่รวมอยู่ในเงื่อนไขสำหรับxถูกตั้งค่าเป็น FALSE

iris %>% mutate_cond(Species == "setosa", Petal.Length = 88, x = TRUE, new_init = FALSE)

ตัวอย่างนี้แสดงวิธีnew_initตั้งค่าเป็น a listเพื่อเริ่มต้นตัวแปรใหม่หลายตัวด้วยค่าที่ต่างกัน ที่นี่สองตัวแปรใหม่ถูกสร้างขึ้นด้วยการยกเว้นแถวถูกเริ่มต้นโดยใช้ค่าที่แตกต่างกัน ( xinitialised เป็นFALSE, yเป็นNA)

iris %>% mutate_cond(Species == "setosa" & Sepal.Length < 5,
                  x = TRUE, y = Sepal.Length ^ 2,
                  new_init = list(FALSE, NA))

mutate_condฟังก์ชันของคุณแสดงข้อผิดพลาดในชุดข้อมูลของฉันและฟังก์ชันของ Grothendiecks ไม่ทำงาน Error: incorrect length (4700), expecting: 168ดูเหมือนจะเกี่ยวข้องกับฟังก์ชันตัวกรอง
RHA

คุณใส่สิ่งนี้ลงในไลบรารีหรือทำให้เป็นทางการเป็นฟังก์ชันหรือไม่? ดูเหมือนจะไม่ใช่เกมง่ายๆโดยเฉพาะอย่างยิ่งกับการปรับปรุงทั้งหมด
ตำแย

1
ครั้งที่ผมคิดว่าวิธีที่ดีที่สุดที่มี dplyr ในเวลานี้คือการรวมกลายพันธุ์ด้วยหรือif_else case_when
Simon Jackson

คุณสามารถให้ตัวอย่าง (หรือลิงค์) สำหรับแนวทางนี้ได้หรือไม่?
ตำแย

6

mutate_cond เป็นฟังก์ชันที่ยอดเยี่ยม แต่จะทำให้เกิดข้อผิดพลาดหากมี NA ในคอลัมน์ที่ใช้ในการสร้างเงื่อนไข ฉันรู้สึกว่าการกลายพันธุ์ตามเงื่อนไขควรปล่อยแถวนั้นไว้เฉยๆ สิ่งนี้ตรงกับลักษณะการทำงานของ filter () ซึ่งส่งคืนแถวเมื่อเงื่อนไขเป็น TRUE แต่ละแถวทั้งสองด้วย FALSE และ NA

ด้วยการเปลี่ยนแปลงเพียงเล็กน้อยนี้ฟังก์ชันจะทำงานได้อย่างมีเสน่ห์:

mutate_cond <- function(.data, condition, ..., envir = parent.frame()) {
    condition <- eval(substitute(condition), .data, envir)
    condition[is.na(condition)] = FALSE
    .data[condition, ] <- .data[condition, ] %>% mutate(...)
    .data
}

ขอบคุณ Magnus! ฉันใช้สิ่งนี้เพื่ออัปเดตตารางที่มีการดำเนินการและการกำหนดเวลาสำหรับวัตถุทั้งหมดที่ประกอบเป็นภาพเคลื่อนไหว ฉันประสบปัญหา NA เนื่องจากข้อมูลมีความหลากหลายมากจนการกระทำบางอย่างไม่สมเหตุสมผลสำหรับวัตถุบางอย่างดังนั้นฉันจึงมี NAs ในเซลล์เหล่านั้น mutate_cond อื่น ๆ ข้างต้นขัดข้อง แต่โซลูชันของคุณทำงานได้อย่างมีเสน่ห์
Phil van Kleur

หากสิ่งนี้เป็นประโยชน์กับคุณฟังก์ชันนี้จะมีอยู่ใน Smallpackage ที่ฉันเขียนว่า "zulutils" ไม่ได้อยู่ใน CRAN แต่คุณสามารถติดตั้งได้โดยใช้รีโมต :: install_github ("torfason / zulutils")
Magnus

4

ฉันไม่เห็นการเปลี่ยนแปลงใด ๆdplyrที่จะทำให้ง่ายขึ้นมาก case_whenเหมาะสำหรับกรณีที่มีเงื่อนไขและผลลัพธ์ที่แตกต่างกันหลายคอลัมน์ แต่ไม่ได้ช่วยอะไรในกรณีนี้ที่คุณต้องการเปลี่ยนหลายคอลัมน์ตามเงื่อนไขเดียว ในทำนองเดียวกันrecodeจะบันทึกการพิมพ์หากคุณแทนที่ค่าที่แตกต่างกันหลายค่าในคอลัมน์เดียว แต่ไม่ได้ช่วยในการทำหลายคอลัมน์พร้อมกัน สุดท้ายmutate_atฯลฯ ใช้เงื่อนไขกับชื่อคอลัมน์เท่านั้นไม่ใช่แถวในดาต้าเฟรม คุณอาจเขียนฟังก์ชันสำหรับ mutate_at ที่ทำได้ แต่ฉันคิดไม่ออกว่าคุณจะทำให้มันทำงานแตกต่างกันอย่างไรสำหรับคอลัมน์ต่างๆ

ที่กล่าวว่านี่เป็นวิธีที่ผมจะเข้าใกล้มันโดยใช้nestแบบฟอร์มtidyrและจากmappurrr

library(data.table)
library(dplyr)
library(tidyr)
library(purrr)

# Create some sample data
set.seed(1)
dt <- data.table(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                                  replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50))

dt2 <- dt %>% 
  nest(-measure) %>% 
  mutate(data = if_else(
    measure == "exit", 
    map(data, function(x) mutate(x, qty.exit = qty, cf = 0, delta.watts = 13)),
    data
  )) %>%
  unnest()

1
สิ่งเดียวที่ฉันแนะนำคือใช้nest(-measure)เพื่อหลีกเลี่ยงgroup_by
Dave Gruenewald

แก้ไขเพื่อสะท้อนข้อเสนอแนะของ
@DaveGruenewald

4

วิธีแก้ปัญหาอย่างรวบรัดวิธีหนึ่งคือทำการกลายพันธุ์บนชุดย่อยที่กรองแล้วเพิ่มกลับแถวที่ไม่ออกของตาราง:

library(dplyr)

dt %>% 
    filter(measure == 'exit') %>%
    mutate(qty.exit = qty, cf = 0, delta.watts = 13) %>%
    rbind(dt %>% filter(measure != 'exit'))

3

ด้วยการสร้างrlangตัวอย่าง 1a ของ Grothendieck เวอร์ชันที่ปรับเปลี่ยนเล็กน้อยเป็นไปได้โดยไม่จำเป็นต้องมีการenvirโต้แย้งเนื่องจากenquo()รวบรวมสภาพแวดล้อมที่.pสร้างขึ้นโดยอัตโนมัติ

mutate_rows <- function(.data, .p, ...) {
  .p <- rlang::enquo(.p)
  .p_lgl <- rlang::eval_tidy(.p, .data)
  .data[.p_lgl, ] <- .data[.p_lgl, ] %>% mutate(...)
  .data
}

dt %>% mutate_rows(measure == "exit", qty.exit = qty, cf = 0, delta.watts = 13)

2

คุณสามารถแยกชุดข้อมูลและทำการเรียกการกลายพันธุ์ตามปกติในTRUEส่วนนั้น

dplyr 0.8มีฟังก์ชั่นgroup_splitที่แยกตามกลุ่ม (และสามารถกำหนดกลุ่มได้โดยตรงในการโทร) ดังนั้นเราจะใช้ที่นี่ แต่ก็base::splitใช้ได้เช่นกัน

library(tidyverse)
df1 %>%
  group_split(measure == "exit", keep=FALSE) %>% # or `split(.$measure == "exit")`
  modify_at(2,~mutate(.,qty.exit = qty, cf = 0, delta.watts = 13)) %>%
  bind_rows()

#    site space measure qty qty.exit delta.watts          cf
# 1     1     4     led   1        0        73.5 0.246240409
# 2     2     3     cfl  25        0        56.5 0.360315879
# 3     5     4     cfl   3        0        38.5 0.279966850
# 4     5     3  linear  19        0        40.5 0.281439486
# 5     2     3  linear  18        0        82.5 0.007898384
# 6     5     1  linear  29        0        33.5 0.392412729
# 7     5     3  linear   6        0        46.5 0.970848817
# 8     4     1     led  10        0        89.5 0.404447182
# 9     4     1     led  18        0        96.5 0.115594622
# 10    6     3  linear  18        0        15.5 0.017919745
# 11    4     3     led  22        0        54.5 0.901829577
# 12    3     3     led  17        0        79.5 0.063949974
# 13    1     3     led  16        0        86.5 0.551321441
# 14    6     4     cfl   5        0        65.5 0.256845013
# 15    4     2     led  12        0        29.5 0.340603733
# 16    5     3  linear  27        0        63.5 0.895166931
# 17    1     4     led   0        0        47.5 0.173088800
# 18    5     3  linear  20        0        89.5 0.438504370
# 19    2     4     cfl  18        0        45.5 0.031725246
# 20    2     3     led  24        0        94.5 0.456653397
# 21    3     3     cfl  24        0        73.5 0.161274319
# 22    5     3     led   9        0        62.5 0.252212124
# 23    5     1     led  15        0        40.5 0.115608182
# 24    3     3     cfl   3        0        89.5 0.066147321
# 25    6     4     cfl   2        0        35.5 0.007888337
# 26    5     1  linear   7        0        51.5 0.835458916
# 27    2     3  linear  28        0        36.5 0.691483644
# 28    5     4     led   6        0        43.5 0.604847889
# 29    6     1  linear  12        0        59.5 0.918838163
# 30    3     3  linear   7        0        73.5 0.471644760
# 31    4     2     led   5        0        34.5 0.972078100
# 32    1     3     cfl  17        0        80.5 0.457241602
# 33    5     4  linear   3        0        16.5 0.492500255
# 34    3     2     cfl  12        0        44.5 0.804236607
# 35    2     2     cfl  21        0        50.5 0.845094268
# 36    3     2  linear  10        0        23.5 0.637194873
# 37    4     3     led   6        0        69.5 0.161431896
# 38    3     2    exit  19       19        13.0 0.000000000
# 39    6     3    exit   7        7        13.0 0.000000000
# 40    6     2    exit  20       20        13.0 0.000000000
# 41    3     2    exit   1        1        13.0 0.000000000
# 42    2     4    exit  19       19        13.0 0.000000000
# 43    3     1    exit  24       24        13.0 0.000000000
# 44    3     3    exit  16       16        13.0 0.000000000
# 45    5     3    exit   9        9        13.0 0.000000000
# 46    2     3    exit   6        6        13.0 0.000000000
# 47    4     1    exit   1        1        13.0 0.000000000
# 48    1     1    exit  14       14        13.0 0.000000000
# 49    6     3    exit   7        7        13.0 0.000000000
# 50    2     4    exit   3        3        13.0 0.000000000

หากลำดับแถวมีความสำคัญให้ใช้tibble::rowid_to_columnก่อนจากนั้นdplyr::arrangeเปิดrowidและเลือกออกในตอนท้าย

ข้อมูล

df1 <- data.frame(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                                  replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50),
                 stringsAsFactors = F)

2

ฉันคิดว่าคำตอบนี้ไม่ได้ถูกพูดถึงมาก่อน มันทำงานได้เร็วเกือบเท่ากับdata.tableโซลูชัน'ค่าเริ่มต้น' ..

ใช้ base::replace()

df %>% mutate( qty.exit = replace( qty.exit, measure == 'exit', qty[ measure == 'exit'] ),
                          cf = replace( cf, measure == 'exit', 0 ),
                          delta.watts = replace( delta.watts, measure == 'exit', 13 ) )

แทนที่ค่าการแทนที่ด้วยซ้ำดังนั้นเมื่อคุณต้องการให้ค่าของคอลัมน์ที่qtyป้อนลงในคอลัมน์qty.exitคุณต้องมีชุดย่อยqty ด้วย ... ดังนั้นqty[ measure == 'exit']ในการแทนที่ครั้งแรก ..

ตอนนี้คุณอาจไม่ต้องการพิมพ์ซ้ำmeasure == 'exit'ตลอดเวลา ... ดังนั้นคุณสามารถสร้างเวกเตอร์ดัชนีที่มีการเลือกนั้นและใช้ในฟังก์ชันด้านบน

#build an index-vector matching the condition
index.v <- which( df$measure == 'exit' )

df %>% mutate( qty.exit = replace( qty.exit, index.v, qty[ index.v] ),
               cf = replace( cf, index.v, 0 ),
               delta.watts = replace( delta.watts, index.v, 13 ) )

เกณฑ์มาตรฐาน

# Unit: milliseconds
#         expr      min       lq     mean   median       uq      max neval
# data.table   1.005018 1.053370 1.137456 1.112871 1.186228 1.690996   100
# wimpel       1.061052 1.079128 1.218183 1.105037 1.137272 7.390613   100
# wimpel.index 1.043881 1.064818 1.131675 1.085304 1.108502 4.192995   100

1

ด้วยค่าใช้จ่ายในการทำลายด้วยไวยากรณ์ dplyr ตามปกติคุณสามารถใช้withinจากฐาน:

dt %>% within(qty.exit[measure == 'exit'] <- qty[measure == 'exit'],
              delta.watts[measure == 'exit'] <- 13)

ดูเหมือนว่าจะเข้ากันได้ดีกับท่อและคุณสามารถทำอะไรก็ได้ที่คุณต้องการภายใน


สิ่งนี้ไม่ได้ผลตามที่เขียนไว้เนื่องจากงานชิ้นที่สองไม่ได้เกิดขึ้นจริง แต่ถ้าคุณทำdt %>% within({ delta.watts[measure == 'exit'] <- 13 ; qty.exit[measure == 'exit'] <- qty[measure == 'exit'] ; cf[measure == 'exit'] <- 0 })แล้วมันได้ผล
24
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.