Sumowanie czasu - ale logicznie

0

Witam
Jeśli tytuł jest "do niczego" to poproszę o podpowiedz jak to nazwać.
Mam tablice dwuwymiarową w której są zapisany czasy procesu w parach :

  1. 08:25 -> 08:40
  2. 09:00 -> 10:25
  3. 09:35 -> 11:05
  4. 13:02 -> 14:23

Od razu dodam że w rzeczywistości to timestamp'y - podałem w formie czasu dla czytelności.
Chciał bym otrzymać sumę (minuty) wszytkich procesów, ale nie po prostu sumę tylko z uwzględnieniem pokrywających się wartości - jak w przypadku wierszy 2 i 3 czyli ilość minut między wiersz 2[1] a wiersz 3[2]. Takie AND.
PHP bez żadnych framework'ów

2

Ale co oznaczają te timestampy? Czas zakończenia - czas trwania? Jeśli zakończenia to jaki jest czas początkowy? Czy może jets to
START -> KONIEC
Wówczas z czym masz problem? Sumujesz wszystkie różnice KONIEC - POCZATKE i tyle

3

Po pierwsze musisz to wyczyścić z pokrywających się zakresów. Czyli trzeba przelecieć po tym i ustawić start i stop tak, żeby ci się nie porywały zakresy.
A co to za dane? Skąd je bierzesz? Ile tego jest?

1

Jeszce pytanie - czy te dane są po kolei? W sensie - czy może być tak, że masz coś na zasadzie:

8-9
10-11
11-13
9-11

0

Oj - nie wiem na co odpowiadać więc po kolei.
to pary start -> koniec
@katakrowa - nie kumam twojego rozwiązania
@UglyMan - wyciągam z bazy - ile? może z 10-20 wierszy w grupie, i chce brać pod uwagę pokrycia - nie chce pokrytych dodać do siebie tylko ... no niewiem jak to nazwać - wypełnić ... takie AND.
@cerrato - są po kolei - wg pierwszej kolumny (startu)

4

Ja bym to liczył na poziomie bazy danych. Przydatna funkcja to: https://www.sqlservertutorial.net/sql-server-window-functions/sql-server-lead-function/

jak bardzo nie chcesz to lecisz po elementach w pierwszym liczysz różnice czasu, w drugim sprawdzasz, czy start zawiera się w poprzednim zakresie, jeśli tak bierzesz koniec poprzedniego jako początek kolejnego, jeśli nie to bierzesz początek z tego rekordu. i tak do końca tablicy.

3

No to ja bym to zrobił tak:

  1. deklarujesz dwie zmienne, w których będziesz roboczo trzymał czas
  2. pobierasz z pierwszego przedziału czas rozpoczęcia (zmienna START) oraz zakończenia (STOP)
  3. patrzysz na kolejny przedział:
  • jeśli czas startu jest po czasie zakończenia poprzedniego to znaczy, że się nie nakładają - więc obliczasz czas trwania pierwszego przedziału i wracasz do pkt. 2
  • jeśli się nakładają - czas startu zostawiasz bez zmiany, czas zakończenia ustawiasz na późniejszy z tej dwójki i analizujesz kolejny przedział
  1. i tak do końca tablicy, którą chcesz sprawdzić.

Napisałem tak na szybko, mam nadzieję, że nie ma tutaj jakiegoś błędu w logice ;)

0

Dzięki za odpowiedzi.
@UglyMan - Za cienki w uszach jestem na kombinacje w SQL'u :) - ale przyjrzę się - i jak zrozumiem to może zastosuje.
@cerrato - Tak to mam zrobione - myślałem że może jest jakiś trik na uproszczenie tego. Mimo wszytko dziękuje.

2
titako napisał(a):

Mam tablice dwuwymiarową w której są zapisany czasy procesu w parach :

  1. 08:25 -> 08:40
  2. 09:00 -> 10:25
  3. 09:35 -> 11:05
  4. 13:02 -> 14:23
titako napisał(a):

@UglyMan - wyciągam z bazy - ile?

A one muszą być w bazie w takiej postaci? Może podczas wkładania ich do bazy, mógłbyś się już pozbyć nachodzących się par? Zamiast robić inserta, mógłbyś sprawdzić czy nachodzi się z porzednim, i je update'ować. Wtedy mógłbyś po prostu zrobić sumę różnic, i jesteś w domu. Wtedy nawet mógłbyś na to nałożyć index.

1

Mój pomysł to wygenerowanie unikalnych punktów czasowych, a następnie sprawdzenie czy odcinki czasu się w nich kończące są w którymkolwiek z przedziałów.

Implementacja w Javie, kod sklecony na kolanie, proszę nie rzucać kamieniami, to jest tylko proof of concept.

Nie wykluczam, że mogą być jakieś dziury w rozumowaniu.

package timeseries;

import java.time.Duration;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;

public class TimeSeries {

   public static void main(String[] args) {
      List<Pair<LocalTime, LocalTime>> ranges = new ArrayList<>();
//      LocalTime now = LocalTime.now();
//      Pair<LocalTime, LocalTime> range = Pair.of(now, now.plusMinutes(6));
//      Random prng = new Random(42);
//      int n = 3;
//      for (int i = 0; i < n; i++) {
//         ranges.add(Pair.of(now.minusMinutes(prng.nextInt(5)), now.plusMinutes(prng.nextInt(6))));
//      }
      //40
      ranges.add(Pair.of(LocalTime.of(8, 10), LocalTime.of(8, 50)));
      ranges.add(Pair.of(LocalTime.of(8, 12), LocalTime.of(8, 48)));
      //20
      ranges.add(Pair.of(LocalTime.of(9, 10), LocalTime.of(9, 20)));
      ranges.add(Pair.of(LocalTime.of(9, 20), LocalTime.of(9, 30)));
      //20
      ranges.add(Pair.of(LocalTime.of(10, 15), LocalTime.of(10, 25)));
      ranges.add(Pair.of(LocalTime.of(10, 35), LocalTime.of(10, 45)));
      //20+20
      ranges.add(Pair.of(LocalTime.of(11, 00), LocalTime.of(11, 13)));
      ranges.add(Pair.of(LocalTime.of(11, 00), LocalTime.of(11, 20)));
      ranges.add(Pair.of(LocalTime.of(12, 40), LocalTime.of(13, 00)));
      ranges.add(Pair.of(LocalTime.of(12, 45), LocalTime.of(13, 00)));
      //
      Function<LocalTime, Integer> mapper = ti -> (int) Duration.between(LocalTime.of(0, 0), ti).toMinutes();
      List<Integer> uniqueHours = ranges.stream()
                                        .flatMap(pair -> Stream.of(pair.getLeft(), pair.getRight()))
                                        .map(mapper)
                                        .distinct()
                                        .sorted()
                                        .collect(Collectors.toList());
      List<Pair<Integer, Integer>> rangesInt = ranges.stream()
                                                     .map(pair -> Pair.of(mapper.apply(pair.getLeft()), mapper.apply(pair.getRight())))
                                                     .collect(Collectors.toList());
      System.out.println(ranges);
      System.out.println(rangesInt);
      System.out.println(uniqueHours);
      int total = 0;
      for (int i = 1; i < uniqueHours.size(); i++) {
         int s = uniqueHours.get(i - 1);
         int e = uniqueHours.get(i);
         int diff = e - s;
         int sFinal = s;
         int eFinal = e;
         boolean inside = rangesInt.stream().anyMatch(pair -> pair.getLeft() <= sFinal && pair.getRight() >= eFinal);
         if (inside) {
            total += diff;
         }
         System.out.println(String.format(
               "Czy przedział %02d:%02d do %02d:%02d == %03d jest objęty czasem: %s (total: %03d)",
               (s / 60),
               (s % 60),
               (e / 60),
               (e % 60),
               diff,
               (inside ? "tak" : "nie"),
               total));
      }
      System.out.println(total);
   }
}


[(08:10,08:50), (08:12,08:48), (09:10,09:20), (09:20,09:30), (10:15,10:25), (10:35,10:45), (11:00,11:13), (11:00,11:20), (12:40,13:00), (12:45,13:00)]
[(490,530), (492,528), (550,560), (560,570), (615,625), (635,645), (660,673), (660,680), (760,780), (765,780)]
[490, 492, 528, 530, 550, 560, 570, 615, 625, 635, 645, 660, 673, 680, 760, 765, 780]
Czy przedział 08:10 do 08:12 == 002 jest objęty czasem: tak (total: 002)
Czy przedział 08:12 do 08:48 == 036 jest objęty czasem: tak (total: 038)
Czy przedział 08:48 do 08:50 == 002 jest objęty czasem: tak (total: 040)
Czy przedział 08:50 do 09:10 == 020 jest objęty czasem: nie (total: 040)
Czy przedział 09:10 do 09:20 == 010 jest objęty czasem: tak (total: 050)
Czy przedział 09:20 do 09:30 == 010 jest objęty czasem: tak (total: 060)
Czy przedział 09:30 do 10:15 == 045 jest objęty czasem: nie (total: 060)
Czy przedział 10:15 do 10:25 == 010 jest objęty czasem: tak (total: 070)
Czy przedział 10:25 do 10:35 == 010 jest objęty czasem: nie (total: 070)
Czy przedział 10:35 do 10:45 == 010 jest objęty czasem: tak (total: 080)
Czy przedział 10:45 do 11:00 == 015 jest objęty czasem: nie (total: 080)
Czy przedział 11:00 do 11:13 == 013 jest objęty czasem: tak (total: 093)
Czy przedział 11:13 do 11:20 == 007 jest objęty czasem: tak (total: 100)
Czy przedział 11:20 do 12:40 == 080 jest objęty czasem: nie (total: 100)
Czy przedział 12:40 do 12:45 == 005 jest objęty czasem: tak (total: 105)
Czy przedział 12:45 do 13:00 == 015 jest objęty czasem: tak (total: 120)
120


2

Jeżeli chodzi o Twój problem jak to nazwałeś "Takie logiczne AND" to prawidłowo się to nazywa: **overlapping intervals ** - więc jak już wiesz co wpisać w google to będzie łatwiej znaleźć rozwiązanie :), ogólnie musisz posortować Twoje interwały i zmergować te które na siebie nachodzą. Potem będziesz miał listę unikalnych interwałów, które po prostu przelecisz w pętli i pobierzesz ich czas trwania (koniec - początek) - bo te które na siebie już nachodziły nie będą występować.

Jeżeli chodzi o jakiś przykładowy kod to po wpisaniu w google wyskoczyło mi takie coś: https://javabypatel.blogspot.com/2020/09/find-all-overlapping-intervals-among-given-set-of-intervals.html - pewnie tam tylko sprawdza czy na siebie nachodzą, więc trzeba zmodyfikować zeby zwracał wszystkie interwały ale idee zrozumiesz.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.