R中唯一值的累积计数
Posted
技术标签:
【中文标题】R中唯一值的累积计数【英文标题】:Cumulative count of unique values in R 【发布时间】:2013-03-19 20:50:43 【问题描述】:我的数据集的简化版本如下所示:
depth value
1 a
1 b
2 a
2 b
2 b
3 c
我想创建一个新数据集,其中对于“深度”的每个值,我将拥有从顶部开始的唯一值的累积数量。例如
depth cumsum
1 2
2 2
3 3
关于如何做到这一点的任何想法?我对 R 比较陌生。
【问题讨论】:
【参考方案1】:dplyr 尝试。
df %>%
#group_by(group)%>% if you have a third variable and you want to achieve the same results for each group
mutate(cum_unique_entries = cumsum(!duplicated(value))) %>%
group_by(depth) %>% # add group variable for more layers
summarise(cum_unique_entries = last(cum_unique_entries))
【讨论】:
这很好地解决了我的问题,感谢您的回答!【参考方案2】:这是另一个尝试:
numvals <- cummax(as.numeric(factor(mydf$value)))
aggregate(numvals, list(depth=mydf$depth), max)
这给出了:
depth x
1 1 2
2 2 2
3 3 3
它似乎也适用于@Arun 的示例:
depth x
1 1 2
2 2 4
3 3 4
4 4 5
5 5 6
6 6 6
【讨论】:
我不完全确定,但似乎depth
和value
必须同时排序。例如,无论您如何setkey()
this data.table
: mydf = data.table(data.frame(depth=c(1,1,2,2,6,7), value=c("a", "b", "g", "h", "b", "c")))
,此方法都不会计算c
的唯一出现次数。【参考方案3】:
这可以通过使用sqldf 包的单个SQL 语句以相对简洁的方式编写。假设DF
是原始数据框:
library(sqldf)
sqldf("select b.depth, count(distinct a.value) as cumsum
from DF a join DF b
on a.depth <= b.depth
group by b.depth"
)
【讨论】:
假设depth
是数字,这非常有用。如果depth
是日期的字符串或字符串表示形式,就像我的情况一样,这可能是一个非常昂贵的操作。
在许多情况下,速度并不重要,清晰度才是更重要的问题。如果性能很重要,那么您真的必须对其进行测试而不是做出假设,如果发现速度太慢,请添加索引并再次测试。【参考方案4】:
我发现这是使用factor
并仔细设置levels
的完美案例。我将在这里使用data.table
来表达这个想法。确保您的 value
列是 character
(不是绝对要求)。
第 1 步:只需使用 unique
行,即可将您的 data.frame
转换为 data.table
。
require(data.table)
dt <- as.data.table(unique(df))
setkey(dt, "depth") # just to be sure before factoring "value"
第 2 步:将 value
转换为 factor
并强制转换为 numeric
。确保自己设置关卡(这很重要)。
dt[, id := as.numeric(factor(value, levels = unique(value)))]
第 3 步:将键列设置为 depth
以进行子集化,只需选择最后一个值
setkey(dt, "depth", "id")
dt.out <- dt[J(unique(depth)), mult="last"][, value := NULL]
# depth id
# 1: 1 2
# 2: 2 2
# 3: 3 3
第 4 步:由于深度增加的行中的所有值都应至少具有上一行的值,因此您应该使用cummax
来获得最终输出。
dt.out[, id := cummax(id)]
编辑:以上代码仅用于说明目的。实际上,您根本不需要第三列。这就是我编写最终代码的方式。
require(data.table)
dt <- as.data.table(unique(df))
setkey(dt, "depth")
dt[, value := as.numeric(factor(value, levels = unique(value)))]
setkey(dt, "depth", "value")
dt.out <- dt[J(unique(depth)), mult="last"]
dt.out[, value := cummax(value)]
这是一个更棘手的例子和代码的输出:
df <- structure(list(depth = c(1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6),
value = structure(c(1L, 2L, 3L, 4L, 1L, 3L, 4L, 5L, 6L, 1L, 1L),
.Label = c("a", "b", "c", "d", "f", "g"), class = "factor")),
.Names = c("depth", "value"), row.names = c(NA, -11L),
class = "data.frame")
# depth value
# 1: 1 2
# 2: 2 4
# 3: 3 4
# 4: 4 5
# 5: 5 6
# 6: 6 6
【讨论】:
这是dplyr
版本:df %>% arrange(depth) %>% mutate(value = cummax(as.numeric(factor(value, levels = unique(value))))) %>% arrange(depth, desc(value)) %>% distinct(depth)
。
这种方法一般适用于depth
和value
都是字符串值的情况。谢谢!
@Arun 这是一个很棒的解决方案!谢谢!
如果我们有第三个变量叫 group 并且我们想为每个组实现相同的结果?【参考方案5】:
这是另一个使用lapply()
的解决方案。使用unique(df$depth)
创建唯一的depth
值向量,然后对于每个这样的值子集,只有depth
等于或小于特定depth
值的value
值。然后计算唯一value
值的长度。此长度值存储在cumsum
中,然后depth=x
将给出特定深度级别的值。使用do.call(rbind,...)
使其成为一个数据框。
do.call(rbind,lapply(unique(df$depth),
function(x)
data.frame(depth=x,cumsum=length(unique(df$value[df$depth<=x])))))
depth cumsum
1 1 2
2 2 2
3 3 3
【讨论】:
【参考方案6】:一个好的第一步是创建一个TRUE
或FALSE
列,其中TRUE
用于每个值的第一个,FALSE
用于该值的后续出现。这可以使用duplicated
轻松完成:
mydata$first.appearance = !duplicated(mydata$value)
重塑数据最好使用aggregate
。在这种情况下,它表示对 depth
的每个子集中的 first.appearance
列求和:
newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum)
结果将如下所示:
depth first.appearance
1 1 2
2 2 0
3 3 1
不过,这仍然不是一个累积和。为此,您可以使用 cumsum
函数(然后摆脱旧列):
newdata$cumsum = cumsum(newdata$first.appearance)
newdata$first.appearance = NULL
回顾一下:
mydata$first.appearance = !duplicated(mydata$value)
newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum)
newdata$cumsum = cumsum(newdata$first.appearance)
newdata$first.appearance = NULL
输出:
depth cumsum
1 1 2
2 2 2
3 3 3
【讨论】:
以上是关于R中唯一值的累积计数的主要内容,如果未能解决你的问题,请参考以下文章