在数据框中,找到列的每个元素的下一个较小值的索引

Posted

技术标签:

【中文标题】在数据框中,找到列的每个元素的下一个较小值的索引【英文标题】:In a dataframe, find the index of the next smaller value for each element of a column 【发布时间】:2016-11-07 12:51:26 【问题描述】:

问题:

在数据框中,我想创建一个新列作为现有列的下一个较小值的索引。

例如,数据如下所示。已经安排在item, day

  item day val 
1    1   2   3 
2    1   4   2 
3    1   5   1 
4    2   1   1 
5    2   3   2 
6    2   5   3 

首先我想在dplyr 中使用group_by(item) 来选择每个项目的子数据框。

然后对于第 1 行,我向下看这些行并发现第 2 行有一个较小的val。这就是我想要的,所以我记录了该行对应的day。第 2 行类似。

请注意,对于第 3 行和第 6 行,它们是相应子数据帧的最后一行,因此没有下一个较小的值。对于第 4 行和第 5 行,当我向下看这些行时,没有更小的 val

带有新列的数据框应如下所示。

  item day val next.smaller.day
1    1   2   3                4
2    1   4   2                5
3    1   5   1               -1
4    2   1   1               -1
5    2   3   2               -1
6    2   5   3               -1

我想知道是否有任何方法可以使用dplyr 来实现这一点,或者r 中的任何代码,而不是 for 循环。

我发现一个线程询问这个问题的算法。 Given an array, find out the next smaller element for each element 。 这是相关的,并且所提出的算法在时间复杂度方面超过了我,但我仍然发现在我的场景中很难实现。

谢谢!

更新:

这是另一个重新说明我正在寻找的示例。

  item day val next.smaller.day
1    1   2   2                5
2    1   4   3                5
3    1   5   1               -1
4    2   1   3                3
5    2   3   1               -1
6    2   5   2               -1

【问题讨论】:

【参考方案1】:

您可以按项目对数据进行分组,使用diff 函数计算行之间的差异并检查它是否小于零,然后生成一个逻辑向量,您可以使用该逻辑向量获取下一个日。由于您要在第二天取货,因此您将需要 lead 函数将日期列向前移动,以便它可以匹配您要放置它们的行。

旁注:由于diff 函数创建的向量比原始向量短一个元素,并且您总是将每组的最后一行留出,我们可以将diff 结果填充为FALSE 条件。

library(dplyr);
df %>% group_by(item) %>% mutate(smaller = c(diff(val) < 0, F), 
                                 next.smaller.day = ifelse(smaller, lead(day), -1)) %>%
       select(-smaller)

# Source: local data frame [6 x 4]
# Groups: item [2]

#    item   day   val next.smaller.day
#   <int> <int> <int>            <dbl>
# 1     1     2     3                4
# 2     1     4     2                5
# 3     1     5     1               -1
# 4     2     1     1               -1
# 5     2     3     2               -1
# 6     2     5     3               -1

更新

find.next.smaller <- function(ini = 1, vec) 
    if(length(vec) == 1) NA 
    else c(ini + min(which(vec[1] > vec[-1])), 
          find.next.smaller(ini + 1, vec[-1]))
       # the recursive function will go element by element through the vector and find out 
        # the index of the next smaller value.

df %>% group_by(item) %>% mutate(next.smaller.day = day[find.next.smaller(1, val)], 
                                 next.smaller.day = replace(next.smaller.day, is.na(next.smaller.day), -1)) 

# Source: local data frame [6 x 4]
# Groups: item [2]
#
#    item   day   val next.smaller.day
#   <int> <int> <dbl>            <dbl>
# 1     1     2     2                5
# 2     1     4     3                5
# 3     1     5     1               -1
# 4     2     1     1               -1
# 5     2     3     2               -1
# 6     2     5     3               -1

【讨论】:

感谢您的回复。您所做的在给定的示例中有效。但是,下一个较小的值可能不在下一行。例如,如果val2,3,1 并且day2,4,5,我希望输出是5,5,-1。你知道如何实现吗? 查看更新。您可以编写一个递归函数来查找下一个较小值的索引,然后使用 dplyr 函数应用它。 非常感谢!我试图编写一个函数来实现我的目标,但我无法弄清楚。您的解决方案效果很好。让我学习一下你的函数递归性质的精神,并在下次尝试应用它。再次感谢!

以上是关于在数据框中,找到列的每个元素的下一个较小值的索引的主要内容,如果未能解决你的问题,请参考以下文章

SQL取两列的较小值或者较大值

SQL取两列的较小值或者较大值

两个数据帧的数组列的平均值并在pyspark中找到最大索引

如何使用Scala计算Spark中数据框中列的开始索引和结束索引之间的行的平均值?

获取pandas数据框中每一列的前k个元素的索引的快速方法

获取数据框中列的唯一值的计数,这些值最终出现在决策树的每个叶节点中?