Pandas Dataframe неэффективен для проходных столбцов - PullRequest
4 голосов
/ 03 июля 2019

У меня есть данные о количестве осадков (1800 строк и 15 тыс. Столбцов) для каждой ячейки и даты.

                          486335  486336  486337
2019-07-03 13:35:54.445       0       2      22
2019-07-04 13:35:54.445       0       1       1
2019-07-05 13:35:54.445      16       8      22
2019-07-06 13:35:54.445       0       0       0
2019-07-07 13:35:54.445       0      11       0

Я хочу найти даты, когда было достигнуто определенное количество осадков (> 15 мм), и подсчитать днипосле этого события было меньше осадков (<1,1 мм).Вместе с количеством дождя, начальным и конечным периодом, ячейкой и другой информацией, хранящейся в новом кадре данных.</p>

Я пишу цикл for, который выполняет эту работу, но на его завершение ушло несколько дней; (. Я новичок в python, поэтому, возможно, есть несколько советов для других методов.

from datetime import datetime, timedelta, date
import datetime
import pandas as pd

#Existing Data
index_dates =  pd.date_range(pd.datetime.today(), periods=10).tolist()
df = pd.DataFrame({'486335':[0,0,16,0,0,0,2,1,8,2],'486336':[2,1,8,0,11,16,0,1,6,8],'486337':[22,1,22,0,0,0,5,3,6,1]},index=index_dates)
columns = df.columns 
counter_columns = 0

iteration = -1 #Iterations Steps
counter = 10 #10 precipitation values per column
duration = 0 #days with no or less than pp_max_1 rain 
count = False

index_list = df.index #Index for updating df / Integear
period_range = 0  #Amount of days after Event without much rain Integear
period_amount = 0 #Amount of PP in dry days except event Integear
event_amount = 0.0  #Amount of heavy rainfall on the event date Float
pp = 0 #actual precipitation
pp_sum = 0.0 #mm
pp_min = 15.0 #mm min pp for start to count dry days until duration_min_after
pp_max_1 = 0.11 #max pp for 1 day while counting dry days
dry_days = 0 #dry days after event

for x in df:
    for y in df[x]:
        iteration = iteration + 1
        if iteration == counter:
            iteration = 0
            counter_columns = counter_columns + 1
            print("column :",counter_columns, "finished")
        if y >= pp_min and count == False:
            duration = duration + 1
            count = True
            start_period = index_list[iteration]
            event_amount = y
            index = iteration
            pp_sum = pp_sum + y
        elif y >= pp_min and count == True or y >= pp_max_1 and count == True:
            end_period = index_list[iteration]
            dry_periods = dry_periods.append({"start_period":start_period ,"end_period":end_period,"period_range":duration,"period_amount":pp_sum ,"event_amount":event_amount, "cell":columns[counter_columns]},ignore_index=True).sort_values('period_range',ascending=False)
            duration = 0
            count = False
            pp_sum = 0
        elif pp <= pp_max_1 and count == True:
            duration = duration + 1
            pp_sum = pp_sum + y
        else:
            continue
print(dry_periods)

Вывод выглядит так

start_period              end_period period_range  \
0  2019-07-05 13:15:05.545 2019-07-09 13:15:05.545            4   
1  2019-07-05 13:15:05.545 2019-07-09 13:15:05.545            4   
2  2019-07-05 13:15:36.569 2019-07-09 13:15:36.569            4   
3  2019-07-05 13:15:36.569 2019-07-09 13:15:36.569            4   
4  2019-07-05 13:16:16.372 2019-07-09 13:16:16.372            4   
5  2019-07-05 13:16:16.372 2019-07-09 13:16:16.372            4   


    period_amount event_amount    cell  
0            16.0           16  486335  
1            22.0           22  486337  
2            16.0           16  486335  
3            22.0           22  486337  
4            16.0           16  486335  
5            22.0           22  486337  

Ответы [ 2 ]

3 голосов
/ 03 июля 2019

Вы можете избежать итерации по строкам, потому что она плохо масштабируется для больших фреймов данных.

Это другой подход, неуверенный, будет ли он более эффективным для вашего полного кадра данных:

periods=[]
for cell in df.columns:
    sub = pd.DataFrame({'amount': df[cell].values}, index=df.index)
    sub['flag'] = pd.cut(sub['amount'], [0.11, 15, np.inf],
                         labels=[0, 1]).astype(np.float)
    sub.loc[sub.flag>0, 'flag']=sub.loc[sub.flag>0, 'flag'].cumsum()
    sub.flag.ffill(inplace=True)
    x = sub[sub.flag>0].reset_index().groupby('flag').agg(
        {'index':['min', 'max'], 'amount': 'sum'})
    x.columns = ['start', 'end', 'amount']
    x['period_range'] = (x.end - x.start).dt.days + 1
    x['cell'] = cell
    x.reindex(columns=['start', 'end', 'period_range', 'cell'])
    periods.append(x)

resul = pd.concat(periods).reset_index(drop=True)
2 голосов
/ 03 июля 2019

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

duration = 0 #days with no or less than pp_max_1 rain 
count = False

index_list = df.index #Index for updating df / Integear
period_range = 0  #Amount of days after Event without much rain Integear
period_amount = 0 #Amount of PP in dry days except event Integear
event_amount = 0.0  #Amount of heavy rainfall on the event date Float
pp = 0 #actual precipitation
pp_sum = 0.0 #mm
pp_min = 15.0 #mm min pp for start to count dry days until duration_min_after
pp_max_1 = 0.11 #max pp for 1 day while counting dry days
dry_days = 0 #dry days after event
dry_periods= list()

for counter_columns, column in enumerate(df.columns, 1):
    for period, y in df[column].items():
        if not count and y >= pp_min:
            duration += 1
            count = True
            start_period = period
            event_amount = y
            pp_sum += y
        elif count and (y >= pp_min or y >= pp_max_1):
            end_period = period
            dry_periods.append({
                    "start_period":  start_period ,
                    "end_period":    end_period,
                    "period_range":  duration,
                    "period_amount": pp_sum ,
                    "event_amount":  event_amount, 
                    "cell":          column})
            duration = 0
            count =    False
            pp_sum =   0
        elif count and pp <= pp_max_1:
            duration += 1
            pp_sum   += y
    print("column :",counter_columns, "finished")

dry_periods.sort(key=lambda record: record['period_range'])
print(dry_periods)

Изменения:

  • удален доступ index_list [итерация], который, я думаю, может занять некоторое время
  • удалила всю логику счетчика итераций, потому что связанная с ней логика может быть размещена вне внутреннего цикла, таким образом, внутренний цикл становится меньше, хотя, вероятно, на самом деле не так сильно увеличивает производительность
  • счетчик сравнения == Истина не обязательна, вы можете просто написать счетчик в предложении if
  • изменил логику приращения и суммирования с var = var + num на var + = num (это, вероятно, дело вкуса, вы также можете пропустить это, если хотите, это не окажет такого большого влияния на производительность)
  • затем я помещаю логику сортировки вас dry_periods вне цикла, потому что мне кажется, что ваша логика цикла не полагается на сортируемый набор -> возможно, это даже самое большое влияние на производительность

Btw. Поскольку я не знал, как именно определяется dry_periods, я просто использовал его в качестве списка. Пожалуйста, обратите внимание на состояние

elif count and (y >= pp_min or y >= pp_max_1):

выше. Это выглядит подозрительно для меня, но это просто переписанное условие вашей программы. Если все в порядке, возможно, вы можете удалить одно из сравнений, потому что я предполагаю, что pp_min

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