Как реализовать эффективный алгоритм для расчета нескольких различных значений для большого набора данных? - PullRequest
0 голосов
/ 16 февраля 2020

Я пытаюсь найти самый быстрый способ вычислить количество уникальных значений в огромной таблице, где число строк может легко составлять от 100 миллионов до 10 миллиардов. В данном конкретном случае я имею дело со 128-битными целыми числами.

Я пытаюсь понять, почему подход pandas достигает лучшего результата (проверено с 1 миллионом строк), поскольку он, кажется, выполняет операции над уровень столбцов, который кажется неэффективным. Как это должно быть реализовано в C ++? Моя первоначальная попытка создать версию c ++ была чрезвычайно медленной (медленнее, чем Python). Я использовал std: set, std: pair и std: map.

Первая попытка выглядит следующим образом:

import time
from collections import defaultdict as ddict
import pandas as pd

df = pd.DataFrame([])  # Load table with two columns containing 128 bit integers.

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        self.end = time.time()
        self.interval = self.end - self.start
        print("time elapsed:" ,self.interval)


with Timer():
    print(df['left'].nunique())
    print(df['right'].nunique())
    left_grp = df.groupby('left')
    print(left_grp['right'].nunique().max())
    right_grp = df.groupby('right')
    print(right_grp['left'].nunique().max())

Ниже приведен чистый пример Python, который проходит массив через строка за строкой, что, на мой взгляд, должно быть более эффективным. Это всего лишь в 3 раза медленнее, чем pandas версия.

with Timer():
    uniques1 = set()
    uniques2 = set()

    uniques3 = ddict(set)
    uniques4 = ddict(set)

    for i in range(len(ndarray)):
        uniques1.add(ndarray[i]['left'])
        uniques2.add(ndarray[i]['right'])
        uniques3[ndarray[i]['left']].add(ndarray[i]['right'])
        uniques4[ndarray[i]['right']].add(ndarray[i]['left'])

    print(len(uniques1))
    print(len(uniques2))
    print(max(len(v) for v in uniques3.values()))
    print(max(len(v) for v in uniques4.values()))

Любой совет, как эффективно реализовать приведенный выше чистый код Python в c ++? Моя попытка использования c ++ приведена ниже.

#include <stdint.h>
#include <map>
#include <bits/stdc++.h>
#include <algorithm>

typedef std::pair<uint64_t, uint64_t> uint128_t;
typedef std::set<uint128_t> set128_t;
typedef std::map<uint128_t, set128_t > map128_t;


namespace nunique_highperf{
    int get_max(const map128_t& map) {
        int best = 0;
        auto it = map.begin();

        while (it != map.end()) {
            best = std::max(best, (int)it->second.size());
            it++;
        }

        return best;
    }

    void default_update(map128_t &map, uint128_t left, uint128_t right) {
        set128_t temp;
        map.emplace(left, temp);
        temp = map[left];
        temp.insert(right);
        map[left] = temp;
    }

    void uniques_from_table(uint64_t **sessions, int rows) {
        set128_t uniques1;
        set128_t uniques2;
        map128_t uniques3;
        map128_t uniques4;

        for (int i=0; i<rows; i++) {
            uint128_t left = std::make_pair(sessions[i][0], sessions[i][1]);
            uint128_t right = std::make_pair(sessions[i][2], sessions[i][3]);

            uniques1.insert(left);
            uniques2.insert(right);

            default_update(uniques3, left, right);
            default_update(uniques4, right, left);
        }

        printf("%d\n", uniques1.size());
        printf("%d\n", uniques2.size());
        printf("%d\n", get_max(uniques3));
        printf("%d\n", get_max(uniques4));
    }
}

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

РЕДАКТИРОВАТЬ: добавлен код C ++

1 Ответ

0 голосов
/ 16 февраля 2020

Решение было на самом деле простым.

Замена функции default_update на это:

    void default_update(map128_t &map, uint128_t left, uint128_t right) {
        set128_t temp;
        auto temp_pair = map.emplace(left, temp);
        temp_pair.first->second.insert(right);
    }

добились цели.

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