在包含已排序组

时间:2018-05-26 18:46:46

标签: r

显示包含一些已排序的分组数据的行的数据框。需要引入一个新列,其值取决于某些列的值。

如果第一个值为零,那么如果没有这样的值,则组的所有值都将获得第一个非零值或NA。否则,如果第一个值不为零,则分配固定值,例如, -1

示例输入数据框:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0))

创建 calc 列的示例输出数据框:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0),
 calc = c(6, 6, 6, 6, NA, NA, -1, -1))

提前谢谢。

P.S。:基础R是优选的

4 个答案:

答案 0 :(得分:3)

以下是基础R的一种方法:

df$calc <- unlist(tapply(df$value, df$name, function(x) rep(if(x[1]==0) x[x!=0][1] else -1, length(x))))

......还有更好的方法:

df$calc <- ave(df$value, df$name, FUN = function(x) if(x[1]==0) x[x!=0][1] else -1)

两部分可以理解:

首先,编写一个符合您所需条件的函数。

 doit <- function(x) if(x[1]==0) x[x!=0][1] else -1

其次,在ave中使用它:

 ave(df$value, df$name, FUN=doit)

|编辑|

  

如果我想分配给&#34; calc&#34;如何修改功能?列来自另一列的值,例如&#34; value2&#34;,对应于第一个非零&#34;值&#34;?

此处ave不再为您提供帮助,您需要split数据框并重新加入。

df$value2 <- 101:108
do.call(rbind, lapply(split(df, df$name), function(x) {
  x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
  x
}))

请注意function(x)中的第二行...这是返回整个x而不仅仅是$calc组件。逻辑顺序是:split - &gt; lapply - &gt; do.call但是由于括号的工作方式,它看起来相反。可以使用magrittr中的管道重写这一点,以便保留逻辑顺序(管道LHS %>% RHS将LHS作为第一个参数转发给RHS,因此需要do.call的技巧,我们希望它是第二个论点。)。

library(magrittr)
split(df, df$name) %>%
  lapply(function(x) {
    x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
    x
  }) %>% {do.call(rbind, .)}

答案 1 :(得分:2)

以下是data.table

的选项
library(data.table)
setDT(df)[, calc := if(!value[1]) value[value != 0][1] else -1, name]
df
#   name value calc
#1:    A     0    6
#2:    A     0    6
#3:    A     6    6
#4:    A     3    6
#5:    B     0   NA
#6:    B     0   NA
#7:    C     7   -1
#8:    C     0   -1

答案 2 :(得分:1)

您可以将group_bycase_whendplyr

一起使用
library(tidyverse)

df %>%
  group_by(name) %>%
  mutate(calc = case_when(
    first(value) != 0 ~ -1., 
    max(value) == 0 ~ NA_real_,
    TRUE ~ value[value != 0][1]))

输出:

# A tibble: 8 x 3
# Groups:   name [3]
  name  value  calc
  <fct> <dbl> <dbl>
1 A        0.    6.
2 A        0.    6.
3 A        6.    6.
4 A        3.    6.
5 B        0.   NA 
6 B        0.   NA 
7 C        7.   -1.
8 C        0.   -1.

答案 3 :(得分:0)

我同意@ andrew_reece的回答。 你甚至可以跳过line max(value) == 0 ~ NA_real_,因为calc无论如何都会被设置为NA,所以“写”可能会更短:

df %>% 
  group_by(name) %>% 
  mutate(calc = ifelse(first(value) == 0, value[value != 0][1], -1))