Как указать, что я хочу интервал с наименьшим значением среди всех интервалов, которые удовлетворяют условию - PullRequest
1 голос
/ 02 апреля 2019

Мой последний вопрос (я сделал много за последнее время).

У меня есть df1, который суммирует различные даты и времени. У меня также есть df2, который суммирует температуру воды на разных глубинах с течением времени. Я хочу добавить столбец в df1 с именем Term_depth, показывающий среднюю глубину между двумя последовательными глубинами для определенной даты и времени, ЕСЛИ разница больше 4.5. Моя проблема заключается в том, что иногда для конкретной даты и времени в df1 есть два интервала в df2 для этой конкретной даты и времени, которые удовлетворяют тому, что они больше, чем 4.5. В этих ситуациях мне нужна средняя глубина для интервала, который имеет самую низкую температуру на самой большой глубине. То есть, если df2$T15 равно 25, df2$T25 равно 17, а df2$T35 равно 24, меня интересует среднее значение интервала df2$T15 - df2$T25, поскольку на нижнем уровне значение меньше интервал df2$25 - df2$35.

Как пример:

df1<- data.frame(DateTime=c("2016-08-01 08:01:17","2016-08-01 09:17:14","2016-08-01 10:29:31","2016-08-01 11:19:02","2016-08-01 12:22:45","2016-08-01 13:19:27","2016-08-01 14:58:17","2016-08-01 15:29:10","2016-08-01 16:27:13"))
df1$DateTime<- as.POSIXct(df1$DateTime, format = "%Y-%m-%d %H:%M:%S", tz= "UTC")
df1$Round_datetime<- round_date(df1$DateTime, unit = "hour")

df2<- data.frame(DateTime=c("2016-08-01 07:00:00","2016-08-01 08:00:00","2016-08-01 09:00:00","2016-08-01 10:00:00","2016-08-01 11:00:00","2016-08-01 12:00:00","2016-08-01 13:00:00","2016-08-01 14:00:00","2016-08-01 15:00:00","2016-08-01 16:00:00","2016-08-01 17:00:00"),T5=c(27.8,27.0,27.5,27.1,27.0,26.8,26.3,26.0,26.3,27.1,26.7),
T15=c(24.2,22.0,23.4,23.1,22.7,22.5,21.5,22.0,22.3,24.4,25.4),T25=c(19.5,21.0,20.0,19.5,19.6,16.0,16.3,16.2,16.7,16.4,23.1),T35=c(17.3,16.0,16.0,16.5,16.7,16.3,16.7,16.9,16.7,21.4,18.2))
df2$DateTime<- as.POSIXct(df2$DateTime, format = "%Y-%m-%d %H:%M:%S", tz= "UTC")

df1
             DateTime      Round_datetime
1 2016-08-01 08:01:17 2016-08-01 08:00:00
2 2016-08-01 09:17:14 2016-08-01 09:00:00
3 2016-08-01 10:29:31 2016-08-01 10:00:00
4 2016-08-01 11:19:02 2016-08-01 11:00:00
5 2016-08-01 12:22:45 2016-08-01 12:00:00
6 2016-08-01 13:19:27 2016-08-01 13:00:00
7 2016-08-01 14:58:17 2016-08-01 15:00:00
8 2016-08-01 15:29:10 2016-08-01 15:00:00
9 2016-08-01 16:27:13 2016-08-01 16:00:00

df2
              DateTime   T5  T15  T25  T35
1  2016-08-01 07:00:00 27.8 24.2 19.5 17.3 # One interval bigger than `4.5`
2  2016-08-01 08:00:00 27.0 22.0 21.0 16.0 # Two intervals bigger than `4.5`
3  2016-08-01 09:00:00 27.5 23.4 20.0 16.0 # Zero intervals bigger than `4.5`
4  2016-08-01 10:00:00 27.1 23.1 19.5 16.5 # Zero intervals bigger than `4.5`
5  2016-08-01 11:00:00 27.0 22.7 20.6 15.7 # One interval bigger than `4.5`
6  2016-08-01 12:00:00 26.8 22.5 16.0 16.3 # One interval bigger than `4.5`
7  2016-08-01 13:00:00 26.3 21.5 16.3 21.6 # Three intervals bigger than `4.5`.
8  2016-08-01 14:00:00 26.0 22.0 16.2 16.9 # One interval bigger than `4.5`
9  2016-08-01 15:00:00 26.3 22.3 16.7 16.7 # One interval bigger than `4.5`
10 2016-08-01 16:00:00 27.1 24.4 16.4 21.4 # Two intervals bigger than 4.5
11 2016-08-01 17:00:00 26.7 25.4 23.1 18.2 # One interval bigger than `4.5`

Хотелось бы получить:

df1
             DateTime      Round_datetime Term_depth
1 2016-08-01 08:01:17 2016-08-01 08:00:00         30
2 2016-08-01 09:17:14 2016-08-01 09:00:00         NA
3 2016-08-01 10:29:31 2016-08-01 10:00:00         NA
4 2016-08-01 11:19:02 2016-08-01 11:00:00         NA
5 2016-08-01 12:22:45 2016-08-01 12:00:00         20
6 2016-08-01 13:19:27 2016-08-01 13:00:00         20
7 2016-08-01 14:58:17 2016-08-01 15:00:00         20
8 2016-08-01 15:29:10 2016-08-01 15:00:00         20
9 2016-08-01 16:27:13 2016-08-01 16:00:00         20

Как я могу это сделать?

Я попробовал цикл for. Проблема в том, что он принимает самый глубокий интервал по умолчанию. Например, df1$Term_depth[6] или df1$Term_depth[9] назначаются как 30 вместо 20.

Здесь у вас есть код и результат с циклом for:

for (i in 1:nrow(df1)) {

    if(abs(df2$T35[which(df1$Round_datetime[i] == df2$DateTime)] - df2$T25[which(df1$Round_datetime[i] == df2$DateTime)]) > 4.5){
      df1$Term_Depth[i] <- 30
    }else if (abs(df2$T25[which(df1$Round_datetime[i] == df2$DateTime)] - df2$T15[which(df1$Round_datetime[i] == df2$DateTime)]) > 4.5){
      df1$Term_Depth[i] <- 20
    }else if (abs(df2$T15[which(df1$Round_datetime[i] == df2$DateTime)] - df2$T5[which(df1$Round_datetime[i] == df2$DateTime)]) > 4.5){
      df1$Term_Depth[i] <- 10
    }else{
      df1$Term_Depth[i] <- "NA"
    }
  }

df1
             DateTime      Round_datetime Term_Depth
1 2016-08-01 08:01:17 2016-08-01 08:00:00         30
2 2016-08-01 09:17:14 2016-08-01 09:00:00         NA
3 2016-08-01 10:29:31 2016-08-01 10:00:00         NA
4 2016-08-01 11:19:02 2016-08-01 11:00:00         NA
5 2016-08-01 12:22:45 2016-08-01 12:00:00         20
6 2016-08-01 13:19:27 2016-08-01 13:00:00         30 # Should be 20
7 2016-08-01 14:58:17 2016-08-01 15:00:00         20
8 2016-08-01 15:29:10 2016-08-01 15:00:00         20
9 2016-08-01 16:27:13 2016-08-01 16:00:00         30 # Should be 20

Ответы [ 3 ]

1 голос
/ 02 апреля 2019

Мое решение, приведенное ниже, предполагает, что вам нужен интервал с более низкой температурой.

Поскольку это операции построчно, вы можете векторизовать его - циклы не нужны.

# First create three dummy variables with TRUE if an interval is > 4.5
df2$int1 <- df2$T5-df2$T15  > 4.5
df2$int2 <- df2$T15-df2$T25 > 4.5
df2$int3 <- df2$T25-df2$T35 > 4.5

# Then for each pair of intervals where both are TRUE check which has the lower temperature
# (you could probably wrap it up)
# First for T5-T15 vs T15-T25
df2[df2$int1 == TRUE & df2$int2 == TRUE & df2$T15 < df2$T25, "int2"]  <- FALSE
df2[df2$int1 == TRUE & df2$int2 == TRUE & df2$T15 >= df2$T25, "int1"] <- FALSE

# Same for T15-T25 vs T25-T35
df2[df2$int2 == TRUE & df2$int3 == TRUE & df2$T25 < df2$T35, "int3"]  <- FALSE
df2[df2$int2 == TRUE & df2$int3 == TRUE & df2$T25 >= df2$T35, "int2"] <- FALSE

# Same for T5-T15 vs T25-T35
df2[df2$int1 == TRUE & df2$int3 == TRUE & df2$T15 < df2$T35, "int3"]  <- FALSE
df2[df2$int1 == TRUE & df2$int3 == TRUE & df2$T15 >= df2$T35, "int1"] <- FALSE

# Now we have only one TRUE value in each row - we can get the mean depth by matrix multiplication
df2$Term_depth <- as.matrix(df2[, c("int1", "int2", "int3")]) %*% c(10, 20, 30)
df2[df2$Term_depth == 0, "Term_depth"] <- NA

# Merge it to df1
merge(df1, df2[, c("DateTime", "Term_depth")], by.x = "Round_datetime", by.y = "DateTime", all.x = TRUE)

       Round_datetime            DateTime Term_depth
1 2016-08-01 08:00:00 2016-08-01 08:01:17         30
2 2016-08-01 09:00:00 2016-08-01 09:17:14         NA
3 2016-08-01 10:00:00 2016-08-01 10:29:31         NA
4 2016-08-01 12:00:00 2016-08-01 11:35:02         20
5 2016-08-01 12:00:00 2016-08-01 12:22:45         20
6 2016-08-01 13:00:00 2016-08-01 13:19:27         20
7 2016-08-01 15:00:00 2016-08-01 14:58:17         20
8 2016-08-01 16:00:00 2016-08-01 15:30:10         30
9 2016-08-01 16:00:00 2016-08-01 16:27:13         30

Вывод не совпадает с вашим, потому что введенный вами ввод также просто отличается от напечатанного data.frame с.

1 голос
/ 02 апреля 2019
#As function round_date was not found by default I round it here
df1$Round_datetime <- lubridate::round_date(df1$DateTime, unit = "hour")

df1$Term_depth <- as.numeric(apply(df2[,2:5], 1, function(x) { #Take per row
  tt <- which(abs(diff(x))>4.5)  #find where difference bigger than 4.5
  tt[which.min(x[1+tt])]*10  #Take where lowest temperature at the highest depth
}))[match(df1$Round_datetime, df2$DateTime)] #Merge df2 to df1

#             DateTime Term_depth
#1 2016-08-01 08:01:17         30
#2 2016-08-01 09:17:14         NA
#3 2016-08-01 10:29:31         NA
#4 2016-08-01 11:19:02         NA
#5 2016-08-01 12:22:45         20
#6 2016-08-01 13:19:27         20
#7 2016-08-01 14:58:17         20
#8 2016-08-01 15:29:10         20
#9 2016-08-01 16:27:13         20
1 голос
/ 02 апреля 2019

Если существует более одного интервала> 4,5, но вы всегда хотите, чтобы самый высокий из двух интервалов, тогда вы можете использовать вложенный ifelse, чтобы «двигаться вниз по глубине» до тех пор, пока не будет достигнут критерий> 4,5, и он не не имеет значения, есть ли последующие интервалы на более низких глубинах. (Если я правильно понимаю проблему). Создайте эту переменную в df2, затем объедините ее с df1, чтобы связать правильный интервал с правильным временем.

df2$Term_depth <- ifelse(abs(df2$T5-df2$T15) > 4.5, 10,
                   ifelse(abs(df2$T15-df2$T25) > 4.5 ,20,
                     ifelse(abs(df2$T25-df2$T35) > 4.5 ,30,NA)))

df1 <- merge(x = df1 , y = df2[,c("DateTime","Term_depth")], by.x = "Round_datetime" , by.y = "DateTime" , all.x = TRUE)

df1

> df1
       Round_datetime            DateTime Term_depth
1 2016-08-01 08:00:00 2016-08-01 08:01:17         10
2 2016-08-01 09:00:00 2016-08-01 09:17:14         NA
3 2016-08-01 10:00:00 2016-08-01 10:29:31         NA
4 2016-08-01 12:00:00 2016-08-01 11:35:02         20
5 2016-08-01 12:00:00 2016-08-01 12:22:45         20
6 2016-08-01 13:00:00 2016-08-01 13:19:27         10
7 2016-08-01 15:00:00 2016-08-01 14:58:17         20
8 2016-08-01 16:00:00 2016-08-01 15:30:10         20
9 2016-08-01 16:00:00 2016-08-01 16:27:13         20

Мой вывод немного отличается от вашего желаемого результата, но, как я понял, ваши 4,5 интервала ваши #комментарии в каждой строке не полностью совпадают. В любом случае, может быть, этот код предоставит решение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...