Быстрый способ пересечения списка, сгенерированного с помощью цикла for, из кадра данных со значениями словаря с использованием алгоритма Single Pass - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь реализовать алгоритм Single Pass для решения следующей проблемы:

  • для каждого индекса в кадре данных, укажите, какие столбцы содержат это значение
  • имена этих столбцов затем сохраняются в виде списка
  • есть словарь, в котором ключ - это имя столбца в кадре данных, а значение - все остальные имена столбцов в кадре данных
  • для каждого ключа в этом словаре, пересекайте его значение со списком, сгенерированным из шага 2) выше
  • обновить значение этого словарного ключа с результатом пересечения

Пожалуйста, смотрите мой код ниже

def sp_algorithm(self, dataframe, col_dict):

    # for each cursor value, get all columns that has it
    for ind, i in zip(dataframe.index, range(0, len(dataframe.index))):

        # vectorised index operation
        cols_list = dataframe.columns[dataframe.isin([ind]).any()]

        # for each column in col_dict.keys(), intersect its values with cols_list to get the remaining
        # if the current column value is null, then do nothing
        for key_col in col_dict.keys():

            column_val = dataframe.loc[ind, key_col]

            if (column_val == column_val) & (column_val != ''):
                col_dict[key_col] = list(set(col_dict[key_col]).intersection(cols_list))

Словарь, содержащий имена столбцов, выглядит следующим образом:

col_dict = {'col_A': ['col_B', 'col_C'], 'col_B' = ['col_A', 'col_C'], 'col_C': ['col_A', 'col_B']}

Как вы можете видеть, мой код в настоящее время имеет O(n^2) сложность времени, так как там 2 for loop s.

В настоящее время каждая итерация (включая 2 x for loop s) занимает около 0,8 секунды, что, вероятно, не является проблемой для небольшого набора данных. Однако обрабатываемый набор данных состоит из 300k строк и более 80 столбцов.

Моя проблема заключается в следующем: как реализовать один проход на шаг пересечения словаря, чтобы в цикле было только 1 вместо 2?

EDIT Кадр данных будет содержать отсортированный индекс и значения в в порядке возрастания , как показано ниже:

        col_A col_B col_C
index
0        nan     0     0
1          1     nan   1
2          2     nan   2
3        nan     3     3

Итак, моя текущая функция будет проходить цикл для каждого index, ind, чтобы получить имена столбцов cols_list = dataframe.columns[dataframe.isin([ind]).any()] и пересекать их со значениями словаря.

1-я итерация: cols_list = ['col_B', 'col_C'] затем он будет искать значения для каждого ключа в col_dict (только если в столбце есть значение, поэтому для nan оно будет пропущено), пересекает его и обновляет его.

col_dict = {'col_A': ['col_B', 'col_C'], 'col_B' = ['col_C'], 'col_C': ['col_B']}

2-я итерация: col_B пропускается при проверке значения словаря, так как оно nan, а значение словаря остается прежним cols_list = ['col_A', 'col_C'] col_dict = {'col_A': ['col_C'], 'col_B' = ['col_C'], 'col_C': []}

3-я итерация: col_B пропускается при проверке значения словаря, так как оно nan, а значение словаря остается прежним cols_list = ['col_A', 'col_C'] col_dict = {'col_A': ['col_C'], 'col_B' = ['col_C'], 'col_C': []}

4-я итерация: col_A пропускается при проверке значения словаря, так как оно nan, а значение словаря остается прежним cols_list = ['col_B', 'col_C'] col_dict = {'col_A': ['col_C'], 'col_B' = ['col_C'], 'col_C': []}

1 Ответ

0 голосов
/ 16 января 2019

Сила numpy / scipy / pandas заключается в использовании кода C для выполнения большей части работы за вас. Учитывая, что, по вашим словам, есть 300 тыс. Строк и 80 столбцов, я предлагаю вам сначала приложить все усилия, чтобы убедиться, что вы обрабатываете строки в C, а не в python. Таким образом, ваш первый цикл должен быть исключен: не обрабатывайте 300k элементов, используя python.

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

Index   A  B  C  D
  1     0  0  3  0
  2     2  0  1  -1
  3     0  0  0  0
  192   0  0  1  -1

Вы хотите знать, для каждого индекса, есть ли это значение в каком-либо из столбца A, столбца B, столбца C и т. Д. Если любой Значение индекса отображается в столбце, этот столбец имеет значение «ALIVE». ».

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

В моем примере выше столбец A считается живым из-за {2}, а столбец C считается живым из-за {3, 1}, но столбцы B и D не являются живыми, потому что они не содержат значений, которые присутствуют в указателе. Это правильно?

Попробуйте использовать isin, чтобы определить, присутствуют ли значения в столбцах в индексе. Затем используйте any, чтобы свести логические результаты к одному логическому значению, которое определяет, является ли столбец живым:

row_labels = df.index
col_is_alive = df.isin(row_labels).any()  # NB: any(index=0) is default

(ПРИМЕЧАНИЕ. Я не в том месте, где я могу запустить этот код. Он может содержать синтаксис или другие ошибки.)

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

alive_col_names = { name for name in df.columns if col_is_alive[name] }  # Set comprehension

Тем не менее, ваше первоначальное постановка задачи звучит так, будто вы делаете это один раз (в отличие от итеративного обновления групп имен столбцов). В этом случае, вместо того, чтобы пересекать значения словаря (списки каждого имени столбца, кроме ключа), я бы предложил просто вычислить значения напрямую. То есть вычислять пары ключ-> значение напрямую, а не пытаться «пересекать» списки значений с этим списком всех имен столбцов.

col_dict = { key:alive_col_names - {key} for key in alive_col_names}

С другой стороны, если вы каким-то образом итеративно обновляете эти значения, я бы посоветовал вам сделать вашу вторую структуру данных словарем строки -> установить вместо строки -> список, поскольку это даст вам доступ к стандарту установить операции и поведение.

new_col_dict = {}

for key, other_cols in col_dict.items():
    if key not in alive_col_names:
        continue

    new_col_dict[key] = other_cols.interset(alive_col_names)

col_dict = new_col_dict

(Который можно свернуть, используя сложное понимание, но, возможно, не следует в интересах читабельности.)

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