1
votes

Trier .csv dans des tableaux multidimensionnels

J'essaie de lire des valeurs spécifiques (c'est-à-dire des valeurs @ coordonnée XY) à partir d'un fichier .csv et j'ai du mal à définir correctement des tableaux multidimensionnels dans ce .csv.

Voici un exemple du formulaire de my. fichier csv

#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>


using namespace std;


int main()
{   
  int longitude, latitude;                //Coordinates used to specify desired value
  int t;                                  //Each array is associated to a specific time t=0,1,2,3... (corresponds to hourly measured data)
  string value;                           

  vector<string> t;                       //Vector of each block
  vector<string> longitudeArray;          //Line of array, i.e. latitude    

  ifstream file("swh.csv");               //Open file
  if (!file.is_open())                    //Check if file is opened, if not 
  print "File could..."
  {
     cout << "File could not open..." << endl;
     return 1;
  }

  while (getline(file, latitude, latitude.empty()))   //Scan .csv (vertically) and delimit every time a white line occurs
  {
     longitudeArray.clear();
     stringstream ss(latitude);

     while(getline(ss,value,',')         //Breaks line into comma delimited fields //Specify line number (i.e. int latitude) here??
     {
        latitudeArray.push_back(value); //Adds each field to the 1D array //Horizontal vector, i.e. latitude
     }
     t.push_back(/*BLOCK*/)              //Adds each block to a distinct vector t
  }
  cout << t(longitudeArray[5])[6] << endl;    //Output:   5th element of longitudeArray in my 6th block

  return 0;

Ok, en réalité, ce fichier devient très volumineux. Vous pouvez interpréter les lignes = latitudes et colonnes = longitudes et ainsi chaque bloc est une carte de coordonnées mesurées toutes les heures. Les blocs ont généralement la taille de la ligne [361] colonne [720] et les périodes peuvent aller jusqu'à 20 ans (= 24 * 365 * 20 blocs), juste pour vous donner une idée de la taille des données.

Pour structurer cela, j'ai pensé à parcourir le .csv et à définir chaque bloc comme un vecteur t, auquel je peux accéder en choisissant le pas de temps souhaité t = 0,1,2,3 ...

Ensuite, dans ce bloc, je voudrais aller sur une ligne spécifique (c'est-à-dire la latitude) et la définir comme un vecteur longitudeArray.

Le résultat sera une valeur spécifiée de la coordonnée XY au temps Z. p>

Comme vous pouvez le deviner, mon expérience de codage est plutôt limitée et c'est pourquoi ma question réelle pourrait être très simple: comment puis-je organiser mes vecteurs afin de pouvoir appeler n'importe quelle valeur aléatoire?

Ceci est mon code pour l'instant (malheureusement ce n'est pas beaucoup, car je ne sais pas comment continuer ...)

NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
1.23,NaN,2.34,3.45,NaN

NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
1.23,NaN,2.34,3.45,NaN

NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
NaN,NaN,1.23,2.34,9.99
1.23,NaN,2.34,3.45,NaN
1.23,NaN,2.34,3.45,NaN

...

}

Si vous avez un indice, surtout s'il existe une meilleure façon de gérer les gros fichiers .csv, je vous en serais très reconnaissant.

Ps: C ++ est inévitable pour ce projet ...

Tüdelüü, jtotheakob


9 commentaires

Si vous voulez utiliser un peu de STL, je serais enclin à utiliser une carte pour les pas de temps. Vous avez alors un accès aléatoire, plutôt que linéaire dans un vecteur. Vous pourriez alors avoir un tableau 2D à chaque emplacement de la carte.


Voulez-vous vraiment utiliser des chaînes au lieu de valeurs numériques? Vous dites que votre fichier se compose de 45'537'984'000 valeurs, qui même pour les flottants représenteraient 169 Go de données, et pour les chaînes beaucoup plus (environ 8 fois plus dans gcc, si SSO entre en jeu. pour chaque chaîne)


@Yksisarvinen: ici, si elle est stockée sous forme de tableau de caractères, chaque valeur utilisera exactement 4 octets, tandis qu'un double utilise généralement 8, donc la taille est un argument médiocre. Mais je suis d'accord avec vous, les données doivent être converties en double et NaN en double nan.


@SergeBallesta C'est vrai, j'ai pensé à utiliser std :: string comme dans le code, mais char [4] est beaucoup plus efficace en mémoire.


@Yksisarvinen: les anciens programmeurs C pensent à l'ancienne ;-)


@Yksisarvinen J'ai choisi la chaîne, à cause des valeurs NaN. Je veux en fait les convertir en nombres pour un calcul ultérieur.


@SergeBallesta, je ne comprends pas ce que vous entendez en convertissant "... NaN en double nan." - Puis-je définir les lettres comme doubles aussi? Utiliser char est un bon conseil, je l'adopterai au plus vite. Btw, mes valeurs peuvent comprendre jusqu'à 10 chiffres, mais le double devrait convenir alors, non?


@anonmess merci, je vais d'abord devoir lire à ce sujet mais cela semble très prometteur!


Les nombres à virgule flottante ont 3 valeurs spéciales définies: plus inifinity, moins infinity et NaN - Pas un nombre (il y en a plus, mais ceux-ci sont les plus couramment utilisés, lien wikipedia pour plus d'informations). std :: stof et < a href = "https://en.cppreference.com/w/cpp/string/byte/strtof" rel = "nofollow noreferrer"> std :: strtof le reconnaît pour vous , afin que vous puissiez analyser toutes les valeurs à l'aide de celles-ci.


3 Réponses :


0
votes

Comme d'habitude, vous devez d'abord penser en termes de données et d'utilisation des données. Ici, vous avez des valeurs à virgule flottante (qui peuvent être NaN) qui devraient être accessibles en tant que chose 3D le long de la latitude, de la longitude et de l’heure.

Si vous pouvez accepter des index simples (entiers), les méthodes standard en C ++ seraient des tableaux bruts, std :: array et std :: vector . La règle d'or dit alors: si les tailles sont connues au moment de la compilation, les tableaux (ou std :: array si vous voulez une opération sur des tableaux globaux) sont bien, sinon allez avec des vecteurs. Et si vous n'êtes pas sûr que std: vector est votre bête de somme.

Vous finirez donc probablement par un std :: vector >> données , que vous utiliseriez comme données [timeindex] [latindex] [longindex] . Si tout est statique, vous pouvez utiliser un double data [NTIMES] [NLATS] [NLONGS] auquel vous accéderiez plus ou moins de la même manière. Attention si le tableau est grand, la plupart des compilateurs s'étoufferont si vous le déclarez dans une fonction (y compris main), mais cela pourrait être un global dans une unité de compilation (C-ish mais toujours valide en C ++).

Lisez donc le fichier ligne par ligne, en alimentant les valeurs dans votre conteneur . Si vous utilisez des tableaux définis statiquement, affectez simplement chaque nouvelle valeur à sa position, si vous utilisez des vecteurs, vous pouvez ajouter dynamiquement de nouveaux éléments avec push_back .

C'est trop loin de votre courant code pour moi pour vous montrer plus que du code trivial.

La version statique (C-ish) pourrait contenir:

int ntimes = 0;
const int nlats=361;      // may be a non compile time values
const int nlongs=720;     // dito

vector<vector<vector<double>>> data;

int lat, long;

for(;;) {
    data.push_back(vector<vector<double>>);
    for(lat=0; lat<nlats; lat++) {
        data[ntimes].push_back(vector<double>(nlongs));
        for(long=0; long<nlongs; long++) {
            std::cin >> data[time][lat][long];
            for (;;) {
                if (! std::cin) break;
                char c = std::cin.peek();
                if (std::isspace(c) || (c == ',')) std::cin.get();
                else break;
            }
            if (! std::cin) break;
        }
        if (! std::cin) break;
    }
    if (! std::cin) break;
    if (lat!=nlats || long!=nlongs) {
        //Not enough values or read error
        ...
    }
    ntimes += 1;
}

Une version plus dynamique utilisant des vecteurs pourrait être:

#define NTIMES 24*365*20
#define NLATS 361
#define NLONGS 720

double data[NTIMES][NLATS][NLONGS];
...
int time, lat, long;
for(time=0; time<NTIMES; time++) {
    for (lat=0; lat<NLATS; lat++) {
        for (long=0; long<NLONGS; long++) {
            std::cin >> data[time][lat][long];
            for (;;) {
                if (! std::cin) break;
                char c = std::cin.peek();
                if (std::isspace(c) || (c == ',')) std::cin.get();
                else break;
            }
            if (! std::cin) break;
        }
        if (! std::cin) break;
    }
    if (! std::cin) break;
}
if (time != NTIMES) {
    //Not enough values or read error
    ...
}

Ce code traitera avec succès NaN en lui convertissant la valeur spéciale pas un nombre , mais il ne vérifie pas le nombre de champs par ligne. Pour ce faire, lisez une ligne avec std :: getline et utilisez un strstream pour l'analyser.


0 commentaires

0
votes

Merci, j'ai essayé de transférer les deux versions vers mon code, mais je n'ai pas pu le faire fonctionner. Je suppose que mes faibles compétences en codage ne sont pas capables de voir ce qui est évident pour tout le monde. Pouvez-vous nommer les bibliothèques supplémentaires dont je pourrais avoir besoin? Pour std :: isspace j'ai besoin de #include , il manque quelque chose d'autre qui n'est pas mentionné dans mon code ci-dessus?

Pouvez-vous également expliquer comment fonctionne if (std :: isspace (c) || ​​(c == ',')) std :: cin.get (); ? D'après ce que je comprends, il vérifiera si c (quel est le champ de saisie?) Est un espace, et si oui, le bon terme devient automatiquement "vrai" à cause de || ? Quelle conséquence en résulte-t-il?

Enfin, if (! std :: cin) break est utilisé pour arrêter la boucle après avoir atteint le tableau spécifié [time] [lat] [long]?

Quoi qu'il en soit, merci pour votre réponse. Je l'apprécie beaucoup et j'ai maintenant une idée de comment définir mes boucles.


0 commentaires

0
votes

Encore une fois merci à tous pour vos idées. Malheureusement, je n'ai pas pu exécuter le script ... mais ma tâche a légèrement changé, donc le besoin de lire de très grands tableaux n'est plus nécessaire.

Cependant, j'ai une idée de la manière de structurer de telles opérations et je vais très probablement la transférer dans ma nouvelle tâche.

Vous pouvez fermer ce sujet maintenant;)

Acclamations

jtothekaob


0 commentaires