需要帮助优化具有3个数据帧的循环

时间:2016-12-09 15:28:34

标签: python performance loops pandas optimization

第一个数据帧:inj_2014(35040,991),其中列引用对应于所谓的EAN。

          Date                 541448860005060119 541448860003851078 ...
0         2014-01-01 00:00:00                 0.0                0.0
1         2014-01-01 00:15:00                 0.1                0.0
...

第二个数据帧:db(1125,17)。在这里,EAN重新组合在一列中。有比991更多的线,因为它对应于合约规格:如果合约在2014年2月结束并在2014年3月再次开始,则df中有2条线。列sd和ed对应于开始日期和结束日期

                    EAN          sd            ed  ...
0    541448860008422181  2014-07-02    2017-01-03
1    541449200002077458  2012-01-04    2014-05-07
...

第三个数据帧:价格(1125,9)。基本上每个EAN都有不同的价格规格,全年都有变化(Q1-Q2-Q3-Q4)和时间(峰值偏差)

     Q1_peak  Q1_off_peak  ... Q4_off_peak                  EAN
0    82.0264      56.9196          61.9826   541448860008422181
1    85.2736      57,8456          58,7564   541449200002077458
...

我想做什么:将inj_2014中的数字(即注入)乘以价格并将其放入新的数据框中,并考虑到:

  • 如果日期不在合同范围内(或返回0),则不应计算注入的事实
  • 2个不同的合同可能具有相同的EAN,因此2个不同的列应该是输出(例如,对于更近期的合同,EAN_1)
  • 注入的价格乘以的事实取决于日期和EAN

我已经写了一些有用的功能:

def in_contrat(date, sd, ed):
    '''True if date within date limits'''
    if sd < date < ed:
        return True
    else:
        return False

def price_name(date, dates_2014): #dates_2014 = list of the quarter limit dates
    '''returns price name corresponding to the given date'''
    if date < date_2014[1]:
        if peak(date):
            return 'Q1_peak'
        else:
            return 'Q1_off_peak'
    elif date < date_2014[2]:
        if peak(date):
            return 'Q2_peak'
    ...

def in_contrat(date, sd, ed):
    '''True if date within date limits'''
    if sd < date < ed:
        return True
    else:
        return False

def get_index(df, test):
    '''returns list with index occurences of the specific EAN number in the db'''
    index = []
    for i in range(len(df)):
        if df['EAN'][i] == test:
            index.append(i)
    return index

所以使用这些材料我试着编写我的主函数:

def daily_calculation(inj_2014, db, prices):
    list_EAN = []
    for i in range(len(db)):
        EAN = db['EAN'][i]
        if EAN not in list_EAN:
            list_EAN.append(EAN)
            index = get_index(prices, EAN)[0]
        else : 
            index = get_index(prices, EAN)[1]
        for j in range(len(inj_2014)):
            date = inj_2014['Date'][j]
            name = price_name(date, dates_2014)
            EBIQ = prices[name][index]
            valeur_injection = inj_2014[EAN][j]/4000
            if in_contrat(date, db['sd'][i], db['ed'][i]) and inj_2014[EAN][j] != 0:
                results.set_value(j, EAN, (valeur_injection)*EBIQ)
            else:
                results.set_value(j, EAN, 0)
    return results

所以事情是,这似乎有效。但是,考虑到我只计算第一列的时间,我需要80到100小时才能得到我的结果,它们甚至可能是错的。我可以处理几天运行 - 发现错误 - 运行 - 发现错误-...但不是几个月。

我确定有一种方法可以优化这个循环,获得了大量的时间(我已经设法从200h到100h)。但是,我对python / pandas / etc很新,我没有自己优化它的经验;这是一种绝望的呼唤。

1 个答案:

答案 0 :(得分:0)

如果没有更多信息,很难写出确切的答案,但这是一次尝试。

首先,堆叠inj_2014以将列名放入名为EAN的新列中:

inj_stacked = inj_2014.set_index('Date') \
                      .stack() \ 
                      .reset_index(drop=False) \
                      .rename(columns={'level_1': 'EAN', 0: 'injection'})

第二次,与db合并(价格我推荐查询):

inj_stacked = inj_stacked.merge(db, on='EAN', how='outer')

注意:您必须尝试how的不同值。以上是我最好的猜测。

第三次,对您应用的操作进行矢量化:

inj_stacked['in_contrat'] = (inj_stacked['Date'] > inj_stacked['sd']) \
                          & (inj_stacked['Date'] < inj_stacked['ed'])

inj_stacked['price_name'] = inj_stacked['Date'].apply(price_name, 
                                                      args=(dates_2014,))

第四,查询价格:

inj_stacked['price'] = prices.set_index('EAN').lookup(
    inj_stacked['EAN'], 
    inj_stacked['price_name']).values

这都是未经测试的,请根据需要进行调整。