辞書型配列したい(std::map

 1#include "G4SystemOfUnit.hh"
 2
 3#include <map>
 4
 5std::map<std::string, G4int> fHitInt{};
 6fHitInt["run_id"] = fRunID;
 7fHitInt["event_id"] = fEventID;
 8fHitInt["track_id"] = fTrackID;
 9fHitInt["parent_id"] = fTrackParentID;
10fHitInt["step_id"] = fStepID;
11
12std::map<std::string, G4double> fHitDouble{};
13fHitDouble["energy_deposit"] = fEnergyDeposit / MeV;
14fHitDouble["step_x"] = fStepXYZ.getX() / mm;
15fHitDouble["step_y"] = fStepXYZ.getY() / mm;
16fHitDouble["step_z"] = fStepXYZ.getZ() / mm;
17
18std::map<std::string, G4String> fHitString{};
19fHitString["material_name"] = fMaterialName;

std::mapで辞書型の配列を定義できます。 上のサンプルは、G4VHit(を継承してユーザが定義したクラス)のヒット情報を、 カラム名と一緒にまとめるために使った例です。 後述のLTSV形式の文字列を生成するために使ってみました。

LTSV形式したい

LTSV形式は、Labeled Tab-Separated Value の略で、 以下のような構造を持つ形式です。

key1:value    key2:value    key3:value    ...
key1:value    key2:value    key3:value    ...
key1:value    key2:value    key3:value    ...
...

あまりメジャーではない形式かもしれませんが、 それぞれの値がラベル名(=カラム名)を持つのが特徴です。 解析するときにカラム名を別途調べる必要がないため、かなり便利です。

また、データの数を追加/削除したときにも、解析ツールの修正をあまりしなくてすみます。 そもそも、データ自身がカラム名を持っているので、特定の解析ツールへの依存性がないのも利点です。

注釈

LTSV形式は、Apacheのログ解析の方法を調べているときに知りました。 デフォルト形式(common形式 もしくは combined形式)のApacheログをパースするのは大変です。 LTSV形式を使ったカスタムログにしておくと、

 1G4String ToLtsvString()
 2{
 3    std::stringstream ss;
 4    G4bool is_first = true;
 5    for (const auto& pair: fHitInt) {
 6        if (!is_first) {
 7            ss << ",";
 8        };
 9        ss << pair.first << ":" << pair.second;
10        is_first = false;
11    };
12    for (const auto& pair: fHitInt) {
13        ss << "," << pair.first << ":" << pair.second;
14    };
15    for (const auto& pair: fHitInt) {
16        ss << "," << pair.first << ":" << pair.second;
17    };
18    G4String ltsv{ss.str()};
19    return ltsv;
20};

このサンプルでは、Tab-Separatedではなく、Comma-SeparatedとしたLTSV亜種を作成しています。 CSV形式にしておくと Excelなどの表計算ソフトでファイルを開いたり、 Pythonで読み込むときの区切り文字(delimiter)もデフォルトのままでよかったり、 と便利だと思うからです。

key1:value, key2:value, key3:value, ...
key1:value, key2:value, key3:value, ...
key1:value, key2:value, key3:value, ...
...

行頭と行末に,は不要なのでis_first変数をつかって調整しています。

LTSV形式を読み込みたい

 1import csv
 2from pathlib import Path
 3
 4import pandas as pd
 5
 6def ltsv2data(fname: Path)
 7    rows = []
 8    with fname.open("r") as f:
 9        reader = csv.reader(f)
10        for line in reader:
11            row = dict(field.split(":", 1) for field in line)
12            rows.append(row)
13    data = pd.DataFrame(rows)
14    return data
15
16fname = Path("LTSV形式のファイル.csv")
17data = ltsv2data(fname);

作成したLTSV形式のファイルをPythonのCSVモジュールで読み込み、 pandas.DataFrameに変換する関数を置いておきます。