【问题标题】:Find values of a DataFrame that are between the values of two columns on other DataFrame查找位于其他 DataFrame 上的两列值之间的 DataFrame 的值
【发布时间】:2019-09-29 14:12:33
【问题描述】:

我有两个数据框。 dataframe_A 包含用户和给定值:

dfA <- structure(list(User = 1:5, Value = c(54L, 12L, 7L, 123L, 74L)), 
                 row.names = c(NA, -5L), class = "data.frame")
dfA
#>   User Value
#> 1    1    54
#> 2    2    12
#> 3    3     7
#> 4    4   123
#> 5    5    74

dfB 包含值范围和我想添加到 dfA 的乘数:



dfB <- structure(list(Min = c(0L, 50L, 80L, 100L, 120L), 
                      Max = c(50L, 80L, 100L, 120L, 1000L), 
                      Mult = c(0, 0.5, 0.8, 1, 1.2)), 
                 class = "data.frame", row.names = c(NA, -5L))
dfB
#>   Min  Max Mult
#> 1   0   50  0.0
#> 2  50   80  0.5
#> 3  80  100  0.8
#> 4 100  120  1.0
#> 5 120 1000  1.2

所以想要的结果是将 dfB 中的乘数加到 dfB:

dfA
    User Value  Mult 
[1]    1    54   0.5
[2]    2    12     0
[3]    3     7     0
[4]    4   123   1.2
[5]    5    74   0.5

我已经尝试过这段代码(它适用于单个值),但在数据框中不起作用:

dfA$Mult <- print(subset(dfB, dfA$Value > dfB$Min & dfA$Value < dfB$Max)$Mult)

提前致谢!

【问题讨论】:

  • 值 50 所需的 Mult 是什么?在我看来,MinMax 应该为它们减去 1,或者,至少应该指定边缘情况是属于较低的括号还是较高的括号。

标签: r


【解决方案1】:

如果dfB 中的区间通过像示例中的连续区间切割形成更大范围的分区,我们还可以使用findIntervalcutdfA 中的值匹配到dfB 中的间隔。与findInterval

findInterval(x = dfA$Value, vec = c(dfB$Min[1], dfB$Max))
#> [1] 2 1 1 5 2

结合在dfA 中创建一个新的Mult 列,我们可以这样写:

dfA$Mult <- with(dfB, Mult[findInterval(x = dfA$Value, vec = c(Min[1], Max))])

dfA
#>   User Value Mult
#> 1    1    54  0.5
#> 2    2    12  0.0
#> 3    3     7  0.0
#> 4    4   123  1.2
#> 5    5    74  0.5

免责声明:如果dfB 中的间隔不能很好地排列,则使用findInterval 会变得更加乏味,在这种情况下,Ronak 的方法可能更直接。

【讨论】:

    【解决方案2】:

    对于dfA 中的每个value,您可以在base R 中使用sapply

    dfA$mult <- sapply(dfA$Value, function(x) with(dfB, Mult[x >= Min & x <= Max]))
    
    dfA
    #  User Value mult
    #1    1    54  0.5
    #2    2    12  0.0
    #3    3     7  0.0
    #4    4   123  1.2
    #5    5    74  0.5
    

    数据

    dfA <- structure(list(User = 1:5, Value = c(54L, 12L, 7L, 123L, 74L)), 
           row.names = c(NA, -5L), class = "data.frame")
    
    dfB <- structure(list(Min = c(0L, 50L, 80L, 100L, 120L), Max = c(50L, 
           80L, 100L, 120L, 1000L), Mult = c(0, 0.5, 0.8, 1, 1.2)), 
           class = "data.frame", row.names = c(NA, -5L))
    

    【讨论】:

    • 感谢@RonakShah 提供数据代表。 PietroAiorta,将其包含在原始问题中会很好。
    【解决方案3】:

    一些 tidyverse 解决方案。前两个在两个表之间进行交叉连接——这对于大表来说肯定是非常昂贵的——然后将结果过滤到每个原始 dfA 行中应该有一行 Value 在 @ 987654324@ 范围。最后一个肯定会更快,因为它会立即过滤掉dfB,对每个Value 过滤一次,然后使用过滤和堆叠的Mults 定义新的dfA 变量——但它会做一些奇怪的事情嵌套在另一个映射中的并行映射,都在mutate() 中,所以我敢打赌对于那些不太熟悉purrr 的人来说很难阅读。但我希望看到一些比较基准!

    还请注意,该任务还不清楚:Mult 括号之间的边缘情况 - 如果值为 50,Mult 是 0 还是 0.5?这里我选择更高的Mult

    dfA <- structure(list(User = 1:5, Value = c(54L, 12L, 7L, 123L, 74L)), 
                     row.names = c(NA, -5L), class = "data.frame")
    
    dfB <- structure(list(Min = c(0L, 50L, 80L, 100L, 120L), 
                          Max = c(50L, 80L, 100L, 120L, 1000L),
                          Mult = c(0, 0.5, 0.8, 1, 1.2)), 
                     class = "data.frame", row.names = c(NA, -5L))
    
    library(tidyverse)
    
    crossing(dfA, dfB) %>% 
      filter(Value >= Min, Value < Max) %>% 
      select(-Min, -Max)
    #> # A tibble: 5 x 3
    #>    User Value  Mult
    #>   <int> <int> <dbl>
    #> 1     1    54   0.5
    #> 2     2    12   0  
    #> 3     3     7   0  
    #> 4     4   123   1.2
    #> 5     5    74   0.5
    
    # Slightly more verbose and yet slightly DRYer
    crossing(dfA, dfB) %>% 
      filter(list(Value, Min, Max - 1) %>% pmap_lgl(between)) %>% 
      select(-Min, -Max)
    #> # A tibble: 5 x 3
    #>    User Value  Mult
    #>   <int> <int> <dbl>
    #> 1     1    54   0.5
    #> 2     2    12   0  
    #> 3     3     7   0  
    #> 4     4   123   1.2
    #> 5     5    74   0.5
    
    # Definitely faster, and DRY, and yet more verbose as well as harder to read
    dfA %>% mutate(
      Mult = Value %>% map_dbl(
        ~ dfB %>% 
          filter(list(.x, Min, Max - 1) %>% pmap_lgl(between)) %>% 
          pull(Mult)
      )
    )
    #>   User Value Mult
    #> 1    1    54  0.5
    #> 2    2    12  0.0
    #> 3    3     7  0.0
    #> 4    4   123  1.2
    #> 5    5    74  0.5
    

    reprex package (v0.3.0) 于 2019 年 9 月 29 日创建

    【讨论】:

      【解决方案4】:

      代码如下:

      dfA <- structure(list(User = 1:5, Value = c(54L, 12L, 7L, 123L, 74L)), 
                   row.names = c(NA, -5L), class = "data.frame")
      
      dfB <- structure(list(Min = c(0L, 50L, 80L, 100L, 120L), Max = c(50L, 80L, 
                   100L, 120L,1000L), Mult = c(0, 0.5, 0.8, 1, 1.2)), 
                   class = "data.frame", row.names = c(NA, -5L))
      # we add a mult column to dfA and set all its values to NA
      dfA$mult = NA
      # now we create a function which takes input as a single value from dfA
      # and returns the desired multiplier from dfB
      mult_fun = function(x)
      {
        for (j in 1:nrow(dfB)) {
          if(x > dfB$Min[j] & x < dfB$Max[j]) {
            return(dfB$Mult[j])
          }
        }
      }
      # now we use mult_fun and gets multiplier for every value in dfA
      for (i in 1:nrow(dfA)) {
        dfA$mult[i] = mult_fun(dfA$Value[i])
      }
      

      输出:

      > dfA
        User Value mult
      1    1    54  0.5
      2    2    12  0.0
      3    3     7  0.0
      4    4   123  1.2
      5    5    74  0.5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-12
        • 1970-01-01
        • 2022-09-27
        相关资源
        最近更新 更多