【问题标题】:Converting 900 MB .csv into ROOT (CERN) TTree将 900 MB .csv 转换为 ROOT (CERN) TTree
【发布时间】:2015-10-03 21:23:02
【问题描述】:

我是编程和 ROOT (CERN) 的新手,所以请放轻松。简单地说,我想将约 900 MB(11M 行 x 10 列)的 .csv 文件转换为组织良好的 .root TTree。有人可以提供解决此问题的最佳方法吗?

这是一个带有标题的示例数据行(它是 2010 年美国人口普查区的人口和人口密度数据):

"人口普查县代码","人口普查区域代码","人口普查区块代码","县/州","区块质心纬度 (度)","区块质心 W 经度 (度)","区块土地面积(sq mi)","街区土地面积 (sq km)","街区人口","街区人口密度 (人/平方公里)"

1001,201,1000,Autauga AL,32.469683,-86.480959,0.186343,0.482626154,61,126.3918241

我已经粘贴了我到目前为止写的内容。

我在运行时特别想不通这个错误:“C:41:1: error: unknown type name ‘UScsvToRoot’”。

这可能真的很愚蠢,但是您如何在 ROOT 中读取字符串(用于读取县/州名称)?比如数据类型是什么?我只需要使用char吗?我在发呆。

#include "Riostream.h"
#include "TString.h"
#include "TFile.h"
#include "TNtuple.h"
#include "TSystem.h"

void UScsvToRoot() {

   TString dir = gSystem->UnixPathName(__FILE__);
   dir.ReplaceAll("UScsvToRoot.C","");
   dir.ReplaceAll("/./","/");
   ifstream in;
   in.open(Form("%sUSPopDens.csv",dir.Data()));

   Int_t countyCode,tractCode,blockCode;
   // how to import County/State string?
   Float_t lat,long,areaMi,areaKm,pop,popDens;
   Int_t nlines = 0;
   TFile *f = new TFile("USPopDens.root","RECREATE");
   TNtuple *ntuple = new TNtuple("ntuple","data from csv file","countyCode:tractCode:blockCode:countyState:lat:long:areaMi:areaKm:pop:popDens");

   while (1) {
      in >> countyCode >> tractCode >> blockCode >> countyState >> lat >> long >> areaMi >> areaKm >> pop >> popDens;
      if (!in.good()) break;
      ntuple->Fill(countyCode,tractCode,blockCode,countyState,lat,long,areaMi,areaKm,pop,popDens);
      nlines++;
   }

   in.close();

   f->Write();
}`

【问题讨论】:

  • Stackoverflow 问题需要展示您尝试过的内容并针对特定问题提出问题。 “给我代码”类型的问题往往不会被很好地接受。无论如何,您所说的“宏观”是什么意思?您已标记此 C 和 C++,因此您是否希望编写一个预处理器宏来执行您想要的操作。我认为这不会以任何合理的方式实用。
  • 对不起,我是新手。宏基本上是指功能。那不是等价的吗?我已经编辑了帖子以显示我到目前为止所写的内容。
  • @user3502552 C/C++ 中的术语“宏”总是指使用#define 的预处理器定义。它绝对不等同于一个函数。
  • 代码运行了,但我刚刚意识到我刚刚创建了一个空的 ntuple...其中没有数据...天哪。我真的不擅长这个
  • 澄清行话:在 CERN 物理学界,“宏”是一段半一次性的代码,通常用于制作绘图。一般来说,它需要糟糕的编程实践。

标签: c++ csv bigdata root-framework


【解决方案1】:

我想强调 Erik 回答中的一个细节:在 TTree 之前创建 TFile 的事实对程序产生的根文件的大小有影响。我正在处理一个类似的问题(需要将 ~1 GB 的 CSV 文件读取到根树中并保存到文件中,但首先创建了 TTree,然后创建了 TFile 来存储树。生成的根文件比先创建 TTree 再创建 TFile 时大 10 倍。

出现这种行为的原因是 TTree 中分支的压缩比不同。基本上,将树写入内存时不会应用压缩,而将树写入磁盘时会应用更高的压缩率。

参考:https://root-forum.cern.ch/t/ttree-compression-factor-1-00/31850/11

【讨论】:

    【解决方案2】:

    我认为您最好使用root_pandas。在@Erik 的综合答案中,您最终仍然手动指定感兴趣的变量(countryCode/I,...)。哪个有其优点(我只是列出通用的:你知道你会得到什么。如果缺少预期的变量,会出现错误消息,)。但另一方面,它让您有机会引入拼写错误,如果您阅读多个 csv 文件,您将不会注意到其中任何一个有更多变量……最终复制变量名称和确定变量类型是计算机应该非常好的事情在。

    在 root_pandas 你的代码应该是这样的

    import pandas
    df = pandas.read_csv("USPopDens.csv")
    from root_pandas import readwrite
    df.to_root("USPopDens.root")
    

    【讨论】:

      【解决方案3】:

      好的,所以我要试一试,但前面有几个 cmets:

      对于有关 root 的问题,您应该强烈考虑去root homepage,然后去论坛。虽然 stackoverflow 是一个很好的信息来源,但关于根框架的特定问题更适合在根主页上。

      如果你是 root 新手,你应该看看tutorial page;它有许多关于如何使用 root 的各种功能的示例。

      您还应该使用root reference guide,它包含所有根类的文档。

      对于您的代码:如果您查看您正在使用的 TNtuple 类的 documentation,您会在描述中看到它清楚地表明:

      仅限于浮点变量列表的简单树。

      因此尝试将任何字符串存储到 TNtuple 中将不起作用。为此,您需要使用更通用的类TTree

      要读取文件并将信息存储在树中,您有两种选择: 您可以手动定义分支,然后在循环文件时填充树:

      void UScsvToRoot() {
         TString dir = gSystem->UnixPathName(__FILE__);
         dir.ReplaceAll("UScsvToRoot.C","");
         dir.ReplaceAll("/./","/");
         ifstream in;
         in.open(Form("%sUSPopDens.csv",dir.Data()));
      
         Int_t countyCode,tractCode,blockCode;
         char countyState[1024];
         Float_t lat,lon,areaMi,areaKm,pop,popDens;
         Int_t nlines = 0;
         TFile *f = new TFile("USPopDens.root","RECREATE");
         TTree *tree = new TTree("ntuple","data from csv file");
      
         tree->Branch("countyCode",&countyCode,"countyCode/I");
         tree->Branch("tractCode",&tractCode,"tractCode/I");
         tree->Branch("blockCode",&blockCode,"blockCode/I");
         tree->Branch("countyState",countyState,"countyState/C");
         tree->Branch("lat",&lat,"lat/F");
         tree->Branch("long",&lon,"lon/F");
         tree->Branch("areaMi",&areaMi,"areaMi/F");
         tree->Branch("areaKm",&areaKm,"areaKm/F");
         tree->Branch("pop",&pop,"pop/F");
         tree->Branch("popDens",&popDens,"popDens/F");
      
         while (1) {
            in >> countyCode >> tractCode >> blockCode >> countyState >> lat >> lon >> areaMi >> areaKm >> pop >> popDens;
            if (!in.good()) break;
            tree->Fill();
            nlines++;
         }
      
         in.close();
      
         f->Write();
      }
      

      命令TTree::Branch 基本上告诉root

      • 您的分行名称
      • root 将从中读取信息的变量地址
      • 分支的格式

      包含字符串信息的TBranchC 类型,如果您查看TTree documentation 意味着

      • C : 以0字符结束的字符串

      注意我给了字符数组一定的大小,你应该自己看看什么大小适合你的数据。

      您可以使用的另一种可能性是取消 ifstream 并简单地使用 TTreeReadFile 方法,您可以像这样使用它

      #include "Riostream.h"
      #include "TString.h"
      #include "TFile.h"
      #include "TTree.h"
      #include "TSystem.h"
      
      void UScsvToRoot() {
      
         TString dir = gSystem->UnixPathName(__FILE__);
         dir.ReplaceAll("UScsvToRoot.C","");
         dir.ReplaceAll("/./","/");
      
         TFile *f = new TFile("USPopDens.root","RECREATE");
         TTree *tree = new TTree("ntuple","data from csv file");
         tree->ReadFile("USPopDens.csv","countyCode/I:tractCode/I:blockCode/I:countyState/C:lat/F:lon/F:areaMi/F:areaKm/F:pop/F:popDens/F",',');
         f->Write();
      }
      

      您可以阅读section on TTress in the root users guide on 了解更多信息;除其他外,它还有an example using TTree:ReadFile

      如果这有帮助,请告诉我

      【讨论】:

        猜你喜欢
        • 2011-10-24
        • 2016-10-02
        • 2015-04-04
        • 1970-01-01
        • 2012-05-31
        • 2015-05-12
        • 2011-11-23
        • 2018-05-25
        • 2011-04-26
        相关资源
        最近更新 更多