Как я могу векторизовать проверку значений в каждой строке DataFrame для «вида» и обновить счетчик? - PullRequest
1 голос
/ 07 мая 2019

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

import pandas as pd
df = pd.read_csv( 
    "https://gist.githubusercontent.com/ethanagbaker/98062ebc83b3dd2018a1837d3e3b12df/raw/a59cb7645f6ca935e01a8dea04377da28847c365/testData.csv", 
    skiprows=1, header=None, index_col=0 
) 

Столбцы 3-24 - это идентификаторы узлов, которые совместно используют ребро с узлом, указанным индексом строки, где 0 указывает на отсутствие соседа. Таким образом, если столбцы 3 и 4 в строке 1 имеют ненулевые значения, узел 1 имеет преимущество перед указанными узлами. Столбцы 25-32 предназначены для указания количества соседних узлов каждого типа для узла, указанного индексом, и инициализируются как нули. Вот пример того, как эти данные выглядят: https://imgur.com/LtKRM38. Узел 1 относится к типу 6 с 6 соседями: 373, 389, 175, 99, 127 и 167.

У меня есть функциональный код, который перебирает строки, проверяет столбцы, которые определяют соседние узлы, затем ищет их тип в кадре данных и увеличивает столбец count. Это работает с желаемым эффектом, но медленно. Для ясности, счет для типа n указан в столбцах n + 24. Время выполнения составляет около 4 минут на кадре из 500 строк, но мне нужно увеличить его до ~ 50 000 000 строк. Я пытался изменить это, чтобы использовать .apply() или векторизованный подход, но не совсем понимаю, как это сделать. Вот полностью функциональный итеративный подход:

def countNeighbors(contactMap):
    for index, row in contactMap.iterrows():
        for col in range(3,25):
            cellID = row[col]
            if cellID == 0:
                break
            else:
                cellType = contactMap[1][cellID]
                contactMap[24+cellType][index] += 1
    return contactMap

#run the function
contactMapCounted = countNeighbors(contactMap)

contactMap - матрица, описанная выше, и я экспортировал образец здесь . Обратите внимание, что индекс и заголовок включены и важны . Загрузка этого как панды DataFrame должна позволить вам повторить это.

Полагаю, я просто долго смотрел на это, чтобы посмотреть, что здесь делать, но есть ли очевидный способ ускорить это?

Вероятно, уместное редактирование : После более глубокого тестирования кажется, что одна эта функция довольно быстрая, но я использую ее следующим образом, где я замечаю существенное замедление:

n_shuffles = 100
while s < n_shuffles:
        #print(s)
        contactMap_Shuffled = contactMap.sample(frac=1).reset_index(drop=True)
        contactMap_Shuffled.index += 1
        contactMap_Shuffled.loc[:,25:] = 0  #Reset the count cols
        contactMap_Shuffled = countNeighbors(contactMap_Shuffled)
        s += 1

Это сделано для того, чтобы рандомизировать индекс кадра, а затем пересчитать значения счетчика, как описано. Именно здесь я впервые заметил замедление и предположил, что проблема связана с countNeighbors(), но, возможно, это что-то здесь ...

1 Ответ

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

Я не нашел эффективного способа векторизации countNeighbors(), но мог бы примерно вдвое сократить время выполнения, избегая цепного индексирования (см. Возвращение представления вместо копии ) я е. изменив

                cellType = contactMap[1][cellID]
                contactMap[24+cellType][index] += 1

до

                cellType = contactMap.at[cellID, 1]
                contactMap.at[index, 24+cellType] += 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...