Прочтите csv в C ++ и реализуйте метод, аналогичный сводной таблице - PullRequest
0 голосов
/ 05 августа 2020

Предположим, у меня есть такие данные:

Пример ввода

Как преобразовать данные в такой вывод:

желаемый результат

Мне нужно использовать C ++ для реализации, но я только новичок, поэтому я застрял в этом проекте.

1 Ответ

0 голосов
/ 05 августа 2020

Ваш вопрос состоит из 2 частей. И необходимость вкратце поговорить о моделях данных и Excel.

98% людей, работающих с Excel, неправильно используют Excel. Большинство обрабатываемых данных - это реляционные данные. А реляционные данные должны храниться в реляционных базах данных. В Excel было добавлено так много функций и возможностей, как промежуточные итоги или сводные таблицы, чтобы имитировать функциональность базы данных. Но это в основном неправильный подход. Я мог go работать часами, но людям нравится их Excel (я тоже), так что позвольте им это делать.

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

Итак, сначала нам нужно прочитать ваши данные, хранящиеся в файле CSV. Я уже опубликовал массу примеров, как это сделать. Но позвольте мне объяснить еще раз.

Мы будем читать данные из любого потока, например, из потока открытого файла или std::istringstream.

Это мы делаем с помощью функции std::getline. Мы читаем всю строку, а затем разбиваем строку на токены. Для этого мы используем std::vector (для хранения токенов) и std::sregex_token_iterator для перебора подстрок и их сохранения. Эта часть довольно проста.

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

Если мы смотрим на ввод, у нас есть x-значения. Каждое значение x может иметь 0, 1 или более значений y и z. Итак, у нас есть связь. Кстати. Не каждое значение x должно иметь все значения y. Итак, могут быть такие входные данные:

10,5,0.2
10,6,0.5
10,7,0.6
20,5,0.7
20,6,0.8
20,7,0.9
30,2,0.7
20,8,0.8

Эквивалент реляционных данных в STL может быть разработан с использованием ассоциативного контейнера, например std::map или std::unordered_map. Итак, мы выберем тип данных std::map<int, std::map<int, double>> для правильной обработки исходных данных. Первый тип std::map - это значение x. И вторая, связанная часть - это снова std::map со значениями y и z.

После чтения значений и сохранения данных мы получим следующую иерархическую структуру:

содержание карты

Затем для печати нам нужно сначала извлечь все DISTINCT y-значения и сохранить их временно. A std::set - подходящий контейнер для хранения различных значений. Затем мы напечатаем y-значения в качестве заголовка.

Затем мы напечатаем все c -значения, а затем проверим, существует ли z-значение для текущего y-значения. Если да, то мы его распечатаем.

И все это можно сделать с помощью небольшого фрагмента кода:

#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <regex>
#include <set>

const std::regex re{ "," };

std::istringstream sourceFileStream{R"(10,5,0.2
10,6,0.5
10,7,0.6
20,5,0.7
20,6,0.8
20,7,0.9
30,2,0.7
20,8,0.8
)"};

int main() {

    std::map<int, std::map<int, double>> data;


    // Read values -----------------------------------------------------------------------------------
    // Read all lines from source file
    for (std::string line{}; std::getline(sourceFileStream, line); ) {

        // Split the line into tokens
        std::vector token(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});

        // Convert strings to values
        const int x{ std::stoi(token[0]) };
        const int y{ std::stoi(token[1]) };
        const double z{ stod(token[2]) };

        // Store values into map
        data[x][y] = z;
    }

    // Print values ----------------------------------------------------------------------------------

    // Extract all distinct y values. To get unique values, we will use a std::set
    std::set<int> yValues{};
    for (const auto& [x, yz] : data) for (const auto& [y, z] : yz) yValues.insert(y);

    // Print all y values
    for (const int y : yValues) std::cout << '\t' << y;
    std::cout << '\n';

    // Now iterate over all x values
    for (const auto& [x, yz] : data) {

        // Print the x-value for this line
        std::cout << x;

        // Search the z Value for this x value and the y value of the current column
        for (const int y : yValues) {
            std::cout << '\t';

            // If we have a z value for this x value and the current column y value
            if (auto it = yz.find(y); it != yz.end()) std::cout << it->second;
        }
        std::cout << '\n';
    }
    return 0;
}

Обратите внимание, что строка data[x][y] = z; находится под капотом , очень сложное заявление. Пожалуйста, уделите время, чтобы глубоко проанализировать и понять это.

Если возникнут какие-либо вопросы, я с радостью отвечу

...