R - Obtaining the highest/lowest value in a set of columns defined by the value in a different dataframe

南笙酒味 提交于 2020-12-15 06:02:34

问题


I have two dataframes: one (A) containing the start and end dates (Julian date, so a continuous count of days) of an event, and the other (B) containing values at dates from start to beyond the end dates in the first dataframe. The start date in A is stable, the end date varies.

I want to be able to, for each row, identify the value with the greatest magnitude of change (highest and/or lowest values) between the start and end date in the series in B, then write to a new dataframe.

Example dataframes

dfA <- data.frame(ID = c(1,2,3,4,5), 
                  startDate = rep(1001,5),
                  endDate = c(1007, 1003, 1004, 1005, 1006))

dfB <- data.frame(ID = c(1,2,3,4,5),
                  "1001" = c(0.5,0.3,1,2,1.1),
                  "1002" = c(0.9,0.3,0.5,1.0,1.2), 
                  "1003" = c(0.8,0.3,0.1,1,2), 
                  "1004" = c(1,0.7,0.8,0.9,1.1), 
                  "1005" = c(2,1,3,1,4), 
                  "1006" = c(1,0.5,0.1,0.3,2), 
                  "1007" = c(1,2,3,4,5),
                  "1008" = c(0.5,1,2,1,0.3))

So, for ID = 1, I want to find the lowest value in B between 1001 and 1007, the start and end dates. This would then be repeated as ID = 1,2,3...n

Is there a solution in the tidyverse package for this?

Thanks in advance.


回答1:


Inspired by Matt's answer, but taking highest and lowest values inside the time interval (as I understand the question):

test2 <- left_join(dfA, dfB, by = "ID") %>% 
  pivot_longer(-c(ID, startDate, endDate)) %>% 
  mutate(name = str_remove(name, "X")) %>% 
  filter(name >= startDate & name <= endDate) %>% #here we keep only the rows with name between startDate and endDate
  group_by(ID) %>%
  mutate(highest = max(value), 
         lowest = min(value)) %>% 
  select(ID, highest, lowest) %>% 
  distinct()



回答2:


It's hard to tell what your expected output should be, but here's a dplyr/tidyverse approach by joining dataframes:

library(tidyverse)

    left_join(dfA, dfB, by = "ID") %>% 
  pivot_longer(-c(ID, startDate, endDate)) %>% 
  group_by(ID) %>%
  mutate(name = str_remove(name, "X"),
         highest = max(value), 
         lowest = min(value)) %>% 
  filter(name <= endDate) 

This gives us:

     ID startDate endDate name  value highest lowest
   <dbl>     <dbl>   <dbl> <chr> <dbl>   <dbl>  <dbl>
 1     1      1001    1007 1001    0.5       2    0.5
 2     1      1001    1007 1002    0.9       2    0.5
 3     1      1001    1007 1003    0.8       2    0.5
 4     1      1001    1007 1004    1         2    0.5
 5     1      1001    1007 1005    2         2    0.5
 6     1      1001    1007 1006    1         2    0.5
 7     1      1001    1007 1007    1         2    0.5
 8     2      1001    1003 1001    0.3       2    0.3
 9     2      1001    1003 1002    0.3       2    0.3
10     2      1001    1003 1003    0.3       2    0.3
11     3      1001    1004 1001    1         3    0.1
12     3      1001    1004 1002    0.5       3    0.1
13     3      1001    1004 1003    0.1       3    0.1
14     3      1001    1004 1004    0.8       3    0.1
15     4      1001    1005 1001    2         4    0.3
16     4      1001    1005 1002    1         4    0.3
17     4      1001    1005 1003    1         4    0.3
18     4      1001    1005 1004    0.9       4    0.3
19     4      1001    1005 1005    1         4    0.3
20     5      1001    1006 1001    1.1       5    0.3



回答3:


Base R solution:

If all dates in range are present as vectors in dfB:

# Enure all dates in range have a corresponding vector in dfB copy:
jdrng <- seq(min(dfA$startDate, na.rm = TRUE), max(dfA$endDate, na.rm = TRUE))
prod_df <- merge(dfA, dfB, by = "ID")

# Calculate vector indicies to be used in roc, max and min value calcs:
vnidx <- which(grepl("^X\\d+", names(prod_df)))
strtidx <- vnidx[match(prod_df$startDate, jdrng)]
endidx <- vnidx[match(prod_df$endDate, jdrng)]

# Calculate moc, max and min vals:
res <- cbind(ID = prod_df$ID, do.call(rbind, Map(function(x, y, z) {
  data.frame(moc = (x[, z] - x[, y]) / x[, y],
             maxval = max(unlist(x[, y:z]), na.rm = TRUE),
             minval = min(unlist(x[, y:z]), na.rm = TRUE))
}, split(prod_df, prod_df$ID), strtidx, endidx)))

If not:

# Ensure all dates in range have a corresponding vector in dfB copy:
jdrng <- seq(min(dfA$startDate, na.rm = TRUE), max(dfA$endDate, na.rm = TRUE))
jdvecs <- as.integer(gsub("\\D+", "", grep("^X\\d+", names(dfB), value = TRUE)))
if(!identical(jdrng, jdvecs)){dfB[,paste0("X", setdiff(jdrng, jdvecs))] <- NA_real_}
prod_df <- merge(dfA,
                 dfB[, c(names(dfB)[!grepl("^X\\d+", names(dfB))],
                         paste0("X", sort(jdrng))),], by = "ID")

# Calculate vector indicies to be used in roc, max and min value calcs:
vnidx <- which(grepl("^X\\d+", names(prod_df)))
strtidx <- vnidx[match(prod_df$startDate, jdrng)]
endidx <- vnidx[match(prod_df$endDate, jdrng)]

# Calculate moc, max and min vals:
res <- cbind(ID = prod_df$ID, do.call(rbind, Map(function(x, y, z) {
  data.frame(moc = (x[, z] - x[, y]) / x[, y],
             maxval = max(unlist(x[, y:z]), na.rm = TRUE),
             minval = min(unlist(x[, y:z]), na.rm = TRUE))
}, split(prod_df, prod_df$ID), strtidx, endidx)))


来源:https://stackoverflow.com/questions/64886149/r-obtaining-the-highest-lowest-value-in-a-set-of-columns-defined-by-the-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!