Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to convert multiple if else loop to list comprehension in python

Tags:

python

pandas

I am using following if loop for creating half an hourly buckets for approx 1 million observations which is taking hell lot of time. Following is my if loop

def half_hourly_buckets(dataframe,time_column):
   dataframe[time_column] = pd.to_datetime(dataframe[time_column],format = '%H:%M:%S').dt.time
   for j in range(len(dataframe)):
    x = dataframe.loc[j,time_column]
    if (x >= datetime.time(0,0,1)) & (x <= datetime.time(0,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "00:00:01 - 00:30:00"
    elif (x >= datetime.time(0,30,1)) & (x <= datetime.time(1,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "00:30:01 - 01:00:00"
    elif (x >= datetime.time(1,0,1)) & (x <= datetime.time(1,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "01:00:01 - 01:30:00"
    elif (x >= datetime.time(1,30,1)) & (x <= datetime.time(2,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "01:30:01 - 02:00:00"
    elif (x >= datetime.time(2,0,1)) & (x <= datetime.time(2,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "02:00:01 - 02:30:00"
    elif (x >= datetime.time(2,30,1)) & (x <= datetime.time(3,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "02:30:01 - 03:00:00"
    elif (x >= datetime.time(3,0,1)) & (x <= datetime.time(3,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "03:00:01 - 03:30:00"
    elif (x >= datetime.time(3,30,1)) & (x <= datetime.time(4,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "03:30:01 - 04:00:00"
    elif (x >= datetime.time(4,0,1)) & (x <= datetime.time(4,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "04:00:01 - 04:30:00"
    elif (x >= datetime.time(4,30,1)) & (x <= datetime.time(5,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "04:30:01 - 05:00:00"
    elif (x >= datetime.time(5,0,1)) & (x <= datetime.time(5,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "05:00:01 - 05:30:00"
    elif (x >= datetime.time(5,30,1)) & (x <= datetime.time(6,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "05:30:01 - 06:00:00"
    elif (x >= datetime.time(6,0,1)) & (x <= datetime.time(6,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "06:00:01 - 06:30:00"
    elif (x >= datetime.time(6,30,1)) & (x <= datetime.time(7,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "06:30:01 - 07:00:00"
    elif (x >= datetime.time(7,0,1)) & (x <= datetime.time(7,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "07:00:01 - 07:30:00"
    elif (x >= datetime.time(7,30,1)) & (x <= datetime.time(8,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "07:30:01 - 08:00:00"
    elif (x >= datetime.time(8,0,1)) & (x <= datetime.time(8,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "08:00:01 - 08:30:00"
    elif (x >= datetime.time(8,30,1)) & (x <= datetime.time(9,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "08:30:01 - 09:00:00"
    elif (x >= datetime.time(9,0,1)) & (x <= datetime.time(9,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "09:00:01 - 09:30:00"
    elif (x >= datetime.time(9,30,1)) & (x <= datetime.time(10,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "09:30:01 - 10:00:00"
    elif (x >= datetime.time(10,0,1)) & (x <= datetime.time(10,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "10:00:01 - 10:30:00"
    elif (x >= datetime.time(10,30,1)) & (x <= datetime.time(11,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "10:30:01 - 11:00:00"
    elif (x >= datetime.time(11,0,1)) & (x <= datetime.time(11,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "11:00:01 - 11:30:00"
    elif (x >= datetime.time(11,30,1)) & (x <= datetime.time(12,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "11:30:01 - 12:00:00"
    elif (x >= datetime.time(12,0,1)) & (x <= datetime.time(12,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "12:00:01 - 12:30:00"
    elif (x >= datetime.time(12,30,1)) & (x <= datetime.time(13,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "12:30:01 - 13:00:00"
    elif (x >= datetime.time(13,0,1)) & (x <= datetime.time(13,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "13:00:01 - 13:30:00"
    elif (x >= datetime.time(13,30,1)) & (x <= datetime.time(14,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "13:30:01 - 14:00:00"
    elif (x >= datetime.time(14,0,1)) & (x <= datetime.time(14,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "14:00:01 - 14:30:00"
    elif (x >= datetime.time(14,30,1)) & (x <= datetime.time(15,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "14:30:01 - 15:00:00"
    elif (x >= datetime.time(15,0,1)) & (x <= datetime.time(15,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "15:00:01 - 15:30:00"
    elif (x >= datetime.time(15,30,1)) & (x <= datetime.time(16,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "15:30:01 - 16:00:00"
    elif (x >= datetime.time(16,0,1)) & (x <= datetime.time(16,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "16:00:01 - 16:30:00"
    elif (x >= datetime.time(16,30,1)) & (x <= datetime.time(17,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "16:30:01 - 17:00:00"
    elif (x >= datetime.time(17,0,1)) & (x <= datetime.time(17,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "17:00:01 - 17:30:00"
    elif (x >= datetime.time(17,30,1)) & (x <= datetime.time(18,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "17:30:01 - 18:00:00"
    elif (x >= datetime.time(18,0,1)) & (x <= datetime.time(18,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "18:00:01 - 18:30:00"
    elif (x >= datetime.time(18,30,1)) & (x <= datetime.time(19,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "18:30:01 - 19:00:00"
    elif (x >= datetime.time(19,0,1)) & (x <= datetime.time(19,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "19:00:01 - 19:30:00"
    elif (x >= datetime.time(19,30,1)) & (x <= datetime.time(20,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "19:30:01 - 20:00:00"
    elif (x >= datetime.time(20,0,1)) & (x <= datetime.time(20,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "20:00:01 - 20:30:00"
    elif (x >= datetime.time(20,30,1)) & (x <= datetime.time(21,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "20:30:01 - 21:00:00"
    elif (x >= datetime.time(21,0,1)) & (x <= datetime.time(21,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "21:00:01 - 21:30:00"
    elif (x >= datetime.time(21,30,1)) & (x <= datetime.time(22,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "21:30:01 - 22:00:00"
    elif (x >= datetime.time(22,0,1)) & (x <= datetime.time(22,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "22:00:01 - 22:30:00"
    elif (x >= datetime.time(22,30,1)) & (x <= datetime.time(23,0,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "22:30:01 - 23:00:00"
    elif (x >= datetime.time(23,0,1)) & (x <= datetime.time(23,30,0)):
        dataframe.loc[j,'half_hourly_bucket'] = "23:00:01 - 23:30:00"
    else:
        dataframe.loc[j,'half_hourly_bucket'] = "23:30:01 - 00:00:00"
return dataframe

Is there any way to avoid this loop and increase the processing speed?

like image 590
Neil Avatar asked Jul 13 '26 22:07

Neil


2 Answers

Firstly, you're doing about twice the number of comparisons you need for this approach. If you don't pass the first test, you already know that

x >= datetime.time(0,30,1))

so you don't have to test this a second time on the next elif.

Secondly, because of the regular buckets you're using, you can work out which bucket you need by taking the number of seconds and using the integer part of the result of dividing that by thirty minutes. Assuming x is a time object you could do something like this:

bucket_number = int((datetime.datetime.combine(datetime.date.min, x) -
                     datetime.datetime.combine(datetime.date.min, datetime.time(0))
                    ).total_seconds() / (30 * 60))
bucket_start = datetime.datetime.combine(datetime.date.min, datetime.time(0)) + \
               datetime.timedelta(seconds = bucket_number * 30 * 60)
bucket_end = datetime.datetime.combine(datetime.date.min, datetime.time(0)) + \
             datetime.timedelta(seconds = (bucket_number + 1) * 30 * 60)
dataframe.loc[j,'half_hourly_bucket'] = "{} - {}".format(bucket_start.strftime('%H:%M:%S'),
                                                         bucket_end.strftime('%H:%M:%S'))

This will remove the need for any tests.

Note: A lot of the hard work here is because it's difficult to work with time objects. If you could use datetime objects instead, this would be a whole lot easier.

like image 123
Tim Avatar answered Jul 16 '26 12:07

Tim


You could take a different approach and use timedelta to define the bucket, which severely simplifies this code:

from datetime import datetime, timedelta


def ceil_dt(dt, delta):
    return dt + (datetime.min - dt) % delta


def floor_dt(dt, delta):
    return dt - (dt - datetime.min) % delta


now = datetime.now()
print(now)
print(
    floor_dt(now, timedelta(minutes=30)), ceil_dt(now, timedelta(minutes=30))
)

https://repl.it/@ryanpcmcquen/TwinHelplessLifecycles-1

floor_dt gives you the beginning of the bucket and ceil_dt gives you the end of it.

like image 28
ryanpcmcquen Avatar answered Jul 16 '26 12:07

ryanpcmcquen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!