【问题标题】:Create a column Status based on the conditions on different states in R根据 R 中不同状态的条件创建列状态
【发布时间】:2018-08-06 20:05:46
【问题描述】:

我有一个这样的数据框:

ID <- c(1,2,3,4,5,5,5,6,6)
States <- c(NA,NA,"All Locked","All Not Locked","All Locked","All Locked"
                   ,"All Not Locked","All Not Locked","All Not Locked")
ToolID <- c(NA,NA,"SWP","SWP","SWP","SWP","SWP","SWP","SWP")
Measurement <- c("Length","Breadth","Width","Height","Time","Time"
                   ,"Time","Mass","Mass")
Location <- c("US","US","UK","UK","US","US","US","UK","UK")

df1 <- data.frame(ID,States,ToolID,Measurement,Location)

我正在尝试使用以下条件对此数据框进行一些数据操作

For each ID (grouped),     
    if States = NA, then the Status = "No Status"
    if States column contains at least(count >=) 1 "All Locked", then the Status = "Lock Status"
    if States column doesn't contain (count =0)  "All Locked", then the Status = "No Lock Status"

我想要的输出是

  ID ToolID Measurement Location         Status
   1     NA      Length       US      No Status
   2     NA     Breadth       US      No status
   3    SWP       Width       UK    Lock Status
   4    SWP      Height       UK No Lock Status
   5    SWP        Time       US    Lock Status
   6    SWP        Mass       UK No Lock Status

我正在尝试这样做,但逻辑错误

df1$Status <- ifelse(df1$States == NA, "No Status",
                ifelse((count(df1$States == "All Locked") >=1),
                  "Lock Status",
                  ifelse((count(df1$States == "All Locked") <1),
                    "No Lock Status", NA)))

有人能指出我正确的方向吗?我想申请我更大的数据集,所以一个快速的解决方案会对我有很大帮助。

【问题讨论】:

  • 看起来您想要汇总输出而不是变异。所以在ungroup 后面加上distinct
  • 我已经对您的代码示例做了不同的处理,但是根据逻辑,当我只需要 1 行时,它返回 2 行 id =5。请在下面查看我对您的解决方案的评论。
  • 删除行的逻辑不清楚
  • 请说明您想要的 ToolID 5 输出;它有 2/3 的锁定状态;根据您最后一个ifelse(..., NA) 的意图,它应该给Status=NA?不是“全部锁定”。看我的回答。

标签: r dataframe dplyr data.table tidyverse


【解决方案1】:

对于NA 元素,使用is.nadplyr::count 适用于data.frame/tbls。

在这里,我们按'ID'分组,检查if在'States'列中至少有一个"All Locked",然后将其更改为整个组的“All Locked”(而不是使用mutate来做这个,在group_byadd=TRUE 中更改它以添加新的分组变量以及现有组),按“ID”和“状态”的频率获取组,然后根据条件,更改值“国家”

library(dplyr)
df1 %>% 
  group_by(ID) %>%
  group_by(States = if("All Locked" %in% States) "All Locked" 
              else States, add = TRUE) %>% 
  mutate(n = n()) %>%
  ungroup %>% 
  mutate(States = c("No Lock Status", "Lock Status")[1+ 
                (States == "All Locked" & n >=1)], 
          States = replace(States, is.na(States), "No Status")) %>%
  select(-n) %>% 
  distinct

【讨论】:

  • 感谢 akrun 的解决方案,但它没有按 ID 分组。我希望每个 ID 只有一行。请查看我想要的输出。我之前没有在我的问题中提到“针对每个 ID”,但现在对其进行了编辑。对此深表歉意。
  • @Sharath 分组只是为了获取取消分组后的频率计数。根据您的逻辑,count 应该按组获取频率
  • @Sharath 您的预期输出是 6 行,而输入数据更多
  • 我的意思是:在上述情况下,ID = 5 有 3 个状态值,“全部锁定”、“全部锁定”、“全部未锁定”,因为状态计数“全部锁定”大于 1,那么每个 ID 应该只返回 1 行,状态为“锁定状态”。对于 id =6,它在状态中没有任何“全部锁定”,因此它应该只返回 1 行,状态为“无锁定状态”。我说得有道理吗?如果让您感到困惑,我很抱歉。
  • 完美。这正是我想要的。非常感谢你的帮助。对于在解释我想要什么时出现的混乱,我深表歉意。
【解决方案2】:

这是一个使用dplyr::case_when 的简短简洁成语。 首先我们计算Status作为“全部锁定”(0..1或NA)状态的汇总统计比例,然后我们立即将Status列回收到相应的字符串输出中:

df1 %>% group_by(ID) %>%

    summarize(ToolID=ToolID[1], Measurement=Measurement[1], Location=Location[1],
      Status = sum( States=="All Locked")/n() ) %>%

    mutate(Status = case_when(
      is.na(Status)         ~ "No Status",
      Status == 1           ~ "Lock Status",
      Status == 0           ~ "No Lock Status",
      between(Status, 0, 1) ~ as.character(NA) ))

输出:

     ID ToolID Measurement Location Status        
  <dbl> <fctr> <fctr>      <fctr>   <chr>         
1  1.00 NA     Length      US       No Status     
2  2.00 NA     Breadth     US       No Status     
3  3.00 SWP    Width       UK       Lock Status   
4  4.00 SWP    Height      UK       No Lock Status
5  5.00 SWP    Time        US       NA            
6  6.00 SWP    Mass        UK       No Lock Status

【讨论】:

  • 注意 ToolID 5 的输出;它有 2/3 的锁定状态;根据您最后一个ifelse(..., NA) 的意图,它应该给Status=NA?不是“全部锁定”。
【解决方案3】:

any() 函数非常适合聚合,在这里。 使用查找表连接NATRUEFALSE 分别转换为 OP 期望的 Status 值。

该方法可以以data.table 语法和dplyr 样式实现。

创建查找表

这将被data.tabledplyr 变体使用。

library(data.table)
lut <- data.table(st = c(NA, TRUE, FALSE), 
                  Status = c("No Status", "Lock Status", "No Lock Status"))

data.table版本

library(data.table)
# aggregate by ID
agg <- setDT(df1)[, .(st = any(States == "All Locked")), by = ID][
  #  join with lookup table
  lut, on = "st"][, -"st"]
# join with df1 to prepend other columns
unique(df1[, -"States"])[agg, on = "ID"]
   ID ToolID Measurement Location         Status
1:  1   <NA>      Length       US      No Status
2:  2   <NA>     Breadth       US      No Status
3:  3    SWP       Width       UK    Lock Status
4:  5    SWP        Time       US    Lock Status
5:  4    SWP      Height       UK No Lock Status
6:  6    SWP        Mass       UK No Lock Status

dplyr版本

library(dplyr)
agg <-df1 %>% 
  group_by(ID) %>% 
  summarize(st = any(States == "All Locked")) %>% 
  left_join(lut) %>% 
  select(-st)
df1 %>% 
  select(-States) %>%  
  unique() %>% 
  left_join(agg)
  ID ToolID Measurement Location         Status
1  1   <NA>      Length       US      No Status
2  2   <NA>     Breadth       US      No Status
3  3    SWP       Width       UK    Lock Status
4  4    SWP      Height       UK No Lock Status
5  5    SWP        Time       US    Lock Status
6  6    SWP        Mass       UK No Lock Status

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 2018-08-23
    • 2017-06-17
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多