在多维数组中对 .csv 进行排序

问题描述 投票:0回答:3

我正在尝试从 .csv 文件中读取特定值(即值@坐标 XY),并努力寻找在该 .csv 中定义多维数组的正确方法。

这是我的 .csv 文件中的表单示例

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

...

好吧,实际上,这个文件变得非常大。您可以解释行=纬度和列=经度,因此每个块都是每小时测量的坐标图。这些块的大小通常为行[361]列[720],时间段最长可达 20 年(=24*365*20 块),只是为了让您了解数据大小。

为了构建这个结构,我想到扫描 .csv 并将每个块定义为向量 t,我可以通过选择所需的时间步长 t=0,1,2,3...

然后,在这个块中,我想转到特定的线(即纬度)并将其定义为向量经度数组。

结果应是时间 Z 处坐标 XY 的指定值。

正如您可能猜到的那样,我的编码经验相当有限,这就是为什么我的实际问题可能非常简单:如何排列我的向量以便能够调用任何随机值?

这是我到目前为止的代码(遗憾的是它不多,因为我不知道如何继续......)

#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;

}

如果您有任何提示,特别是如果有更好的方法处理大型 .csv 文件,我将非常感激。

Ps:这个项目不可避免地需要 C++...

图德鲁, jtotheakob

c++ multidimensional-array vector
3个回答
0
投票

像往常一样,您应该首先考虑数据和数据使用情况。这里有浮点值(可以是 NaN),应该可以沿着纬度、经度和时间作为 3D thing 进行访问。

如果您可以接受简单(整数)索引,C++ 中的标准方法将是原始数组,

std::array
std::vector
。经验法则是:如果在编译时数组的大小(或者如果您想在全局数组上进行操作则
std::array
)就可以,否则就使用向量。如果不确定
std:vector
是您的主力。

因此您可能会以

std::vector<std::vector<std::vector<double>>> data
结尾,您可以将其用作
data[timeindex][latindex][longindex]
。如果一切都是静态的,您可以使用
double data[NTIMES][NLATS][NLONGS]
,您将或多或少以相同的方式访问它。请注意,如果数组很大,如果您在函数(包括 main)中声明它,大多数编译器都会阻塞,但它可能是一个编译单元内的全局变量(C 风格,但在 C++ 中仍然有效)。

因此,逐行读取文件,将值输入到您的容器中。如果您使用静态定义的数组,只需在其位置分配每个新值,如果您使用向量,您可以使用

push_back
动态添加新元素。

这与您当前的代码相差太远,我无法向您展示更多简单的代码。

静态(C-ish)版本可能包含:

#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
    ...
}

使用向量的更动态的版本可能是:

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;
}

此代码将成功处理 NaN,将其转换为特殊的“不是数字”值,但它不会检查每行的字段数。为此,请使用 std::getline 读取一行并使用

strstream
对其进行解析。
    


0
投票
std::isspace

,我确实需要

#include <cctype>
,上面我的代码中没有提到的其他内容还缺少吗?

您还能解释一下

if (std::isspace(c) || (c == ',')) std::cin.get();

是如何工作的吗?据我了解,它将检查 c (这是输入字段?) 是否是空格,如果是,则正确的术语会自动变为“true”,因为

||
?这样做会产生什么后果?

最后,

if (! std::cin) break

用于在到达指定的数组[time][lat][long]后停止循环?


无论如何,谢谢您的回复。我真的很感激,现在我知道如何定义我的循环了。


0
投票

但是,我已经了解如何构建此类操作,并且很可能会将其转移到我的新任务中。

您现在可以关闭此主题了;)

干杯

jtothekaob

© www.soinside.com 2019 - 2024. All rights reserved.