Как сгруппировать массив по дате для цикла?

Мне нужно найти подходящий период в каждом году, вычислить его продолжительность и сумму температур. У меня есть срез массива за несколько лет, он выглядит так:

Табл.1

Начало Конец Продолжительность Сумма температур Полугодие
2018-01-01 2018-03-19 78 -1631.8 1
2018-03-20 2018-03-20 1 0.0 1
2018-03-21 2018-04-05 16 -157.6 1
2018-04-06 2018-04-06 1 0.9 1
2018-04-07 2018-04-11 5 -16.8 1
2018-04-12 2018-04-12 1 1.3 1
2018-04-13 2018-04-20 8 -34.6 1
2018-04-21 2018-04-21 1 1.0 1
2018-04-22 2018-04-24 3 -11.2 1
2018-04-25 2018-05-01 7 23.7 1
2018-05-02 2018-05-09 8 -32.8 1
2018-05-10 2018-05-16 7 21.1 1
2018-05-17 2018-05-18 2 -4.8 1
2018-05-19 2018-10-15 150 1839.0 1
2018-10-16 2018-10-18 3 -2.8 2
2018-10-19 2018-10-23 5 5.4 2
2018-10-24 2018-10-24 1 -1.8 2
2018-10-25 2018-10-29 5 13.9 2
2018-10-30 2019-03-13 134 -2558.3 2
2019-03-14 2019-03-14 1 0.8 1
2019-03-15 2019-03-29 15 -66.6 1
2019-03-30 2019-04-01 3 3.9 1
2019-04-02 2019-04-05 4 -5.8 1
2019-04-06 2019-04-06 1 0.0 1
2019-04-07 2019-04-07 1 -2.0 1
2019-04-08 2019-04-09 2 2.4 1
2019-04-10 2019-04-17 8 -58.2 1
2019-04-18 2019-04-18 1 0.6 1
2019-04-19 2019-04-22 4 -19.7 1
2019-04-23 2019-04-23 1 3.6 1
2019-04-24 2019-05-02 9 -27.7 1
2019-05-03 2019-05-08 6 29.4 1
2019-05-09 2019-05-10 2 -1.2 1
2019-05-11 2019-10-09 153 1929.1 1
2019-10-10 2019-10-12 3 -6.9 2
2019-10-13 2019-10-17 5 20.7 2
2019-10-18 2020-03-15 150 -2154.3 2
2020-01-01 2020-03-15 75 -961.4 1
2020-03-16 2020-03-16 1 0.2 1
2020-03-17 2020-03-29 13 -86.1 1
2020-03-30 2020-03-30 1 1.0 1
2020-03-31 2020-04-01 2 -6.8 1
2020-04-02 2020-10-06 188 2287.6 1
2020-10-07 2020-10-13 7 -18.9 2
2020-10-14 2020-10-18 5 4.2 2
2020-10-19 2020-10-21 3 -3.8 2
2020-10-22 2020-10-22 1 1.9 2
2020-10-23 2020-11-07 16 -114.3 2
2020-11-08 2020-11-10 3 3.5 2
2020-11-11 2020-12-31 51 -962.4 2

Я разбиваю этот массив на года. Каждый год прогоняю через код.

За дату устойчивого перехода весной принимается первый день периода, сумма положительных отклонений которого превышает сумму отрицательных отклонений любого из последующих периодов с отрицательными отклонениями. Осенью наоборот - концом периода будет считаться первый день того периода, сумма отрицательных отклонений которого превышает сумму положительных отклонений любого из последующих периодов с такими отклонениями. Чтобы найти самый большой период с температурой выше 0 я написал такие условия:

# переменные для условий
a_max = 1
a = 0 # сюда записывается искомая сумма из начального периода
b = 0
c = 0
d = 0 # сюда записывается искомая сумма из конечного периода
d_max = 0

# условие для нахождения начала периода
for i in sort_df.loc[sort_df['Полугодие'] == 1]['Сумма температур']:
    if i >= 0:
        if i > a > abs(b):
            a = a
        if i > a and abs(b) > a:
            a = i
        if a > i > abs(b):
            a = a
        if i > a_max:
            a_max = i
        if a < abs(b) < a_max:
            a = a_max
    if i < 0:
        if abs(i) > a:
            b = i
            a = 0
        if abs(i) < a:
            b = i

# условие для нахождения конца периода
for i in sort_df.loc[sort_df['Полугодие'] == 2]['Сумма температур']:
    if i >= 0:
        if i >= abs(d):
            c = i
            d = d_max
        if i < abs(d):
            d = d
            c = i
    if i < 0:
        if i < d_max:
            d_max = i
        if c == 0:
            d = i
        if abs(i) > abs(d) and c > abs(d):
            d = i
        if abs(i) > abs(d) and c < abs(d):
            d = d

Возможно они написаны некрасиво и громоздко, но ничего лучше я пока не придумал и они работают для всех возможных случаев. Далее находим индексы значений в a и d:

idx_a = sort_df[sort_df['Сумма температур'] == a].index[0]  # индекс начальной даты
idx_d = sort_df[sort_df['Сумма температур'] == d].index[0]  # индекс конечной даты

Потом находим начальную и конечную дату, продолжительность и сумму периода для положительных значений:

start = sort_df.at[idx_a, 'Начало']  # начальная дата
end = sort_df.at[idx_d, 'Начало']  # конечная дата
delta = end - start  # продолжительность периода
summa = sort_df[idx_a:(idx_d+1)].loc[sort_df['Сумма температур'] >= 0, 'Сумма температур'].sum()  # сумма температур

После этого записываем эти переменные в уже созданный фрейм с заголовками:

warm_tot.loc[len(warm_tot)] = [start, end, delta, summa]  # записываем строку во врейм

Результатом должно быть:

Табл. 2

Начало Конец Продолжительность Сумма
2018-05-19 2018-10-30 164 days 1858.3
2019-05-03 2019-10-18 168 days 1979.2
2020-04-02 2020-10-07 188 days 2287.6

Всю эту процедуру неудобно делать отдельно для каждого года. Я пытался проверить код на массиве, содержащий данные за 50 лет, в итоге он записал мне одну строку с периодом с 1971 по 2006 год (хотя что-то подобное стоило ожидать). Как можно вписать этот код в один цикл так, чтобы он отдельно проходил по каждому году массива как показан в Табл. 1, а итог записывал в новый массив, где будет один период для каждого года?


Ответы (1 шт):

Автор решения: Дмитрий Емельянов

Получилось так, возможно у кого-то будут идеи получше...

# переменные для условий
a_max = 0
a = 0  # сюда записывается искомая сумма из начального периода
b = 0
c = 0
d = 0  # сюда записывается искомая сумма из конечного периода
d_max = 0

for i, g in sort_warm.groupby('Year'):
    # условие для нахождения начала периода
    for n in g.loc[g['Half'] == 1]['Sum']:
        if n >= 0:
            if n > a > abs(b):
                a = a
            if n > a and abs(b) > a:
                a = n
            if a > n > abs(b):
                a = a
            if n > a_max:
                a_max = n
        if n < 0:
            if abs(n) > a and abs(n) > a_max:
                b = n
                a = 0
                a_max = 0
            if a < abs(n) < a_max:
                b = n
                a = a_max
            if abs(n) < a:
                b = n
    # условие для нахождения конца периода
    for n in g.loc[g['Half'] == 2]['Sum']:
        if n >= 0:
            if n > abs(d) and n > abs(d_max):
                c = n
                d = 0
                d_max = 0
            if abs(d) < n < abs(d_max):
                c = n
                d = d_max
            if n < abs(d):
                d = d
                c = n
        if n < 0:
            if n < d_max:
                d_max = n
            if c == 0 and d == 0:
                d = n
            if c == 0 and d != 0:
                d = d
            if abs(n) > abs(d) and c > abs(d):
                d = n
            if abs(n) > abs(d) > c:
                d = d
            if abs(n) < abs(d):
                d = d
    idx_a = g.loc[(g.Sum == a) & (g.Half == 1)].index[0]  # индекс начальной даты
    idx_d = g.loc[(g.Sum == d) & (g.Half == 2)].index[0]  # индекс конечной даты
    start = sort_warm.at[idx_a, 'Start']  # начальная дата
    end = sort_warm.at[idx_d, 'Start']  # конечная дата
    delta = end - start  # Period периода
    summa = sort_warm[idx_a:(idx_d + 1)].loc[sort_warm['Sum'] >= 0, 'Sum'].sum()  # сумма температур
    warm_tot.loc[len(warm_tot)] = [start, end, delta, summa]  # записываем строку во фрейм
    a_max = 0
    a = 0
    b = 0
    c = 0
    d = 0
    d_max = 0
→ Ссылка