Как эффективно обновить группу значений строк в DataFrame? Как сделать этот алгоритм масштабируемым? - PullRequest
1 голос
/ 08 апреля 2019

У меня есть некоторые проблемы эффективности с алгоритмом, который я собираюсь объяснить фрагментами:

  1. Сначала создается df_fs.Я создаю случайный DataFrame здесь, чтобы он работал с примером

    import pandas as pd
    import numpy as np
    import random as rd
    import string
    
    R = 2500    # ROWS / 2
    C = 100     # COLUMNS
    NPROF = 1   # NUMBER OF PROFILES, up to 6
    
    STNNBR = 'STNNBR'
    d = {}
    for x in range(C):
        key = ''.join(rd.choices(string.ascii_uppercase + string.digits, k=10))
        nan_list = [np.nan] * R
        float_list = [float(1000 * rd.random()) for i in range(R)]
        l = nan_list + float_list
        rd.shuffle(l)
        d[key] = l
    
    d[STNNBR] = [int(200 * rd.random()) for i in range(R*2)]
    
    df_fs = pd.DataFrame(d)
    
  2. Список cur_plot_cols указывает имя столбцов, с которыми мы будем работать:

    pos_list = []
    while len(pos_list) < 20:
        v = int(C * rd.random())
        if v not in pos_list:
            pos_list.append(v)
    d_keys = list(d.keys())
    cur_plot_cols = [d_keys[p] for p in pos_list]
    
  3. prof_df - это огромный DataFrame, который я инициализирую множеством значений NaN и множеством столбцов.Количество столбцов увеличивается с cur_plot_cols и NFPROF:

    tab_list = ['SALNTY', 'OXYGEN', 'NITRAT', 'PHSPHT', 'SILCAT', 'ALKALI', 'TCARBN', 'PH_TOT', 'CFC_11', 'CFC_12', 'CFC113', 'SF6']
    compound_cols = []
    for tab in tab_list:
        for col in cur_plot_cols:
            for n in range(NPROF):
                compound_cols.append('{}_{}_{}'.format(tab, col, n))
    
    d_aux = {}
    if compound_cols != []:
        d_aux = dict.fromkeys(compound_cols, [])
    prof_df = pd.DataFrame(d_aux)  # init empty columns
    prof_df['INDEX'] = df_fs.index.values
    prof_df = prof_df.set_index(['INDEX'])
    
  4. Больше переменных, которые мне нужны, чтобы пример работал:

    plot_prof_invsbl_points = True
    stt_order_reversed = [31]         # up to 6 elements
    tabs_flags_plots = {
        'NITRAT': {                   # tab name
            'flag': 'NITRAT_FLAG_W',
        },
        'SALNTY': {
            'flag': 'SALNTY_FLAG_W',
        },
    }
    visible_flags = [3, 4, 5, 6]
    
  5. Наконец, проблемный алгоритм, линия, помеченная FIXME, является основной горловиной бутылки

    f = cur_plot_cols + [STNNBR]
    df_fs = df_fs.filter(f)
    
    for tab in tab_list:
        i = NPROF - 1
        for stt in stt_order_reversed:
            for col in cur_plot_cols:
                df_aux = df_fs[(df_fs[STNNBR] == stt) & df_fs[col].notnull()]
                if plot_prof_invsbl_points is False:      # this is never True in this example extracted from the original code
                    if tab in tabs_flags_plots.keys():
                        flag = tabs_flags_plots[tab]['flag']
                        df_aux = df_aux[df_aux[flag].isin(visible_flags)]
                prof_df.loc[df_aux.index.values, '{}_{}_{}'.format(tab, col, i)] = df_aux[col]  # FIXME: this is the main bottle neck
            i -= 1
    

Измерения

Я измерил время с помощьюинструмент line_profile, и вот результат:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    31        13        114.0      8.8      0.0      for tab in tab_list:
    32        12        148.0     12.3      0.0          i = NPROF - 1
    37        24        267.0     11.1      0.0          for stt in stt_order_reversed:
    38       372      12430.0     33.4      0.0              for col in cur_plot_cols:
    39       360   12890156.0  35806.0     13.1                  df_aux = df_fs[(df_fs[STNNBR] == stt) & df_fs[col].notnull()]
    40       360      11687.0     32.5      0.0                  if plot_prof_invsbl_points is False:
    41                                                               flag = self.env.tabs_flags_plots[tab]['flag']
    42                                                               df_aux = df_aux[df_aux[flag].isin(self.env.visible_flags)]
    43       360   85075802.0 236321.7     86.3                  prof_df.loc[df_aux.index.values, '{}_{}_{}'.format(tab, col, i)] = df_aux[col]
    44        12        201.0     16.8      0.0              i -= 1

Любое предложение, чтобы сделать эти строки быстрее?

df_aux = df_fs[(df_fs[STNNBR] == stt) & df_fs[col].notnull()]

prof_df.loc[df_aux.index.values, '{}_{}_{}'.format(tab, col, i)] = df_aux[col]

Примечания

На самом деле реальный индекс, который я использую в DataFrames, это хэши, то есть строки.

Мне нужно обновить столбцы prof_df DataFrame.Но имена столбцов сделаны с параметрами [tab, col, i], мне нужно пройти по ним, чтобы установить столбец, который я хочу обновлять в каждой итерации.Есть ли способ выполнить итерацию быстрее и обновить эти столбцы?Есть альтернатива?

Некоторое время назад я использовал это выражение для присвоения сгруппированных значений:

ml_df['xs{}'.format(n_plot)] = df_p.groupby('STNNBR').apply(lambda x: list(x[col_x_name]))

Но я не знаю, как применить тот же метод здесь, потому что на этот раз мне нужен имя столбца и значение i в левой части задания.

Алгоритм занимает 6 секунд, слишком много.

Ответы [ 2 ]

0 голосов
/ 03 мая 2019

Только со следующим изменением время уменьшилось до половины.Но этого по-прежнему недостаточно:

prof_df.loc[df_aux.index.values, '{}_{}_{}'.format(tab, col, i)] = df_aux[col]
# >>
prof_df.assign(**{'{}_{}_{}'.format(tab, col, i): df_aux[col] })

Затем я извлек назначение из цикла и организовал циклы лучше.Производительность была улучшена в 7 раз.

df_fs = df_fs[df_fs[STNNBR].isin(stt_order_reversed)]
d_temp = {}
for tab in tab_list:
    i = NPROF - 1

    if tab in tabs_flags_plots.keys():
        flag = tabs_flags_plots[tab]['flag']
        df_fs = df_fs[df_fs[flag].isin(visible_flags)]

    for stt in stt_order_reversed:
        df_stt = df_tab[df_tab[STNNBR] == stt]
        for col in cur_plot_cols:
            df_aux = df_stt[col]
            d_temp['{}_{}_{}'.format(tab, col, i)] = df_aux
        i -= 1
prof_df = prof_df.assign(**d_temp)

Примечание : Теперь я хочу сделать это параллельно с Numba или JobLib, но, возможно, я посмотрю на это в будущем.В настоящее время результат достаточно хороший

0 голосов
/ 09 апреля 2019

Я все еще не понимаю ваш запрос, но я думаю, что вы должны попробовать просмотреть цикл, используя перечисление:

http://book.pythontips.com/en/latest/enumerate.html

Это позволит вам использовать значение i кака также имя столбца.

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