テーブルをjsonに変換

テーブルをjsonに変換

JSONに変換したい大規模なデータテーブルがありますが、jq、mlrなどのツールが不足しているawkテクノロジに頼らずにこれらのタスクを実行できるかどうかはわかりません。

サンプルテーブル:

Balance_sheet for AAPL:

                                                        2023-09-30      2022-09-30      2021-09-30      2020-09-30
Treasury Shares Number                                         0.0             NaN             NaN             NaN
Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0

好ましい出力:

{
    "Balance_sheet for AAPL": {
        "Treasury Shares Number": {
            "2023-09-30": "0.0",
            "2022-09-30": "NaN",
            "2021-09-30": "NaN",
            "2020-09-30": "NaN"
        },
        "Ordinary Shares Number": {
            "2023-09-30": "15550061000.0",
            "2022-09-30": "15943425000.0",
            "2021-09-30": "16426786000.0",
            "2020-09-30": "16976763000.0"
        }
    }
}

次の形式も機能しますが、それほど理想的ではありません。

{
    "Balance_sheet for AAPL": {
        "2023-09-30": {
            "Treasury Shares Number": "0.0",
            "Ordinary Shares Number": "15550061000.0"
        },
        "2022-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "15943425000.0"
        },
        "2021-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "16426786000.0"
        },
        "2020-09-30": {
            "Treasury Shares Number": "NaN",
            "Ordinary Shares Number": "16976763000.0"
        }
    }
}

これを行う合理的な方法を知っている人はいますか?

答え1

私は以下を使用しますperl

$ perl -MJSON::PP -ae '
  if (/^(.*):$/) {$sheet = $1}
  elsif (/^\h+\d/) {$n = (@dates = @F)}
  elsif (/^(.*?)((?:\h+)\H+){$n}$/) {
    $i = -$n;
    $j{$sheet}->{$1} = {map {$_ => $F[$i++]} @dates}
  }
  END {print JSON::PP->new->pretty->encode(\%j)}' your-file
{
   "Balance_sheet for AAPL" : {
      "Ordinary Shares Number" : {
         "2023-09-30" : "15550061000.0",
         "2020-09-30" : "16976763000.0",
         "2022-09-30" : "15943425000.0",
         "2021-09-30" : "16426786000.0"
      },
      "Treasury Shares Number" : {
         "2020-09-30" : "NaN",
         "2023-09-30" : "0.0",
         "2021-09-30" : "NaN",
         "2022-09-30" : "NaN"
      }
   }
}

正規表現に基づいて、入力から3種類の行を区別します。

  • :現在の「シート」(最上位オブジェクトのキー)を決定する
  • 1つ以上の水平スペースで+始まり\h、その後に\d小数点の1桁が続く行、この数字は日付(3番目のレベルのオブジェクトのキー)であり、配列に書き込まれ、@datesその数字はに書き込まれます$n
  • 少なくとも$nスペースで区切られたフィールドを含む行は、最後のフィールドの前の部分が2番目のレベルオブジェクトのキーを構成し、キーとして使用し、最後のフィールドを値として使用して、そのキーの3番目のレベル$nオブジェクトを作成します。@dates$n
  • 他のすべて(例入力の空行)は無視されます。

JSONオブジェクトはPerl連想配列を表すため、メンバーの順序はランダムです。各オブジェクトのメンバーがキーに基づいてソートされるcanonicalフラグ()を設定して、一貫した順序を取得できます。JSON::PP->new->pretty->canonical->encode(\%j)

JSONオブジェクトのフィールドの順序がテーブルの順序を反映することが重要な場合(説明を参照)、次のことがperldoc JSON::PPできます。結ぶさまざまな種類のハッシュのこのような配列は、次のようなものを使用します。

$ perl -MData::Dumper -MTie::Hash::Indexed -MJSON::PP -ae '
  BEGIN{tie %j, $m = "Tie::Hash::Indexed"}
  if (/^(.*):$/) {tie my %s, $m; $j{$sheet = $1} = \%s}
  elsif (/^\h+\d/) {$n = (@dates = @F)}
  elsif (/^(.*?)((?:\h+)\H+){$n}$/) {
    tie my %s, $m;
    $i = -$n;
    %s = map {$_ => $F[$i++]} @dates;
    $j{$sheet}->{$1} = \%s
  }
  END {print JSON::PP->new->pretty->encode(\%j)}' your-file
{
   "Balance_sheet for AAPL" : {
      "Treasury Shares Number" : {
         "2023-09-30" : "0.0",
         "2022-09-30" : "NaN",
         "2021-09-30" : "NaN",
         "2020-09-30" : "NaN"
      },
      "Ordinary Shares Number" : {
         "2023-09-30" : "15550061000.0",
         "2022-09-30" : "15943425000.0",
         "2021-09-30" : "16426786000.0",
         "2020-09-30" : "16976763000.0"
      }
   }
}

Tie::Hash::Indexedlibtie-hash-indexed-perlDebianパッケージ)は、順序付けられたハッシュを提供するいくつかのモジュールの1つです。

これが重要な場合は、予想される形式に近い形式のために(for -style Pretty-printing)に:置き換えてください(4つのスペースのインデントとsの後にスペースがありますが、以前はスペースはありません)。prettyindent->indent_length(4)->space_afterindent_length(2)jq

答え2

POSIX awkを使用してください。

$ cat tst.awk
BEGIN {
    inStep = 4
    print "{"
}

sub(/:$/,"") {
    indent = inStep
    printf "%*s\"%s\": {\n", indent, "", $0
    next
}

!numDates && /^[[:space:]]/ {
    numDates = split($0,dates)
    next
}

numDates && match($0,"[[:space:]]+([^[:space:]]+[[:space:]]*){"numDates"}$") {
    indent += inStep
    printf "%s%*s\"%s\": {\n", (numItems++ ? ",\n" : ""), indent, "", substr($0,1,RSTART-1)

    indent += inStep
    $0 = substr($0,RSTART,RLENGTH)
    for ( i=1; i<=numDates; i++ ) {
        printf "%*s\"%s\": \"%s\"%s\n", indent, "", dates[i], $i, (i<numDates ? "," : "")
    }
    indent -= inStep

    printf "%*s}", indent, ""
    indent -= inStep
}

END {
    printf "\n%*s}\n", indent, ""
    print "}"
}

$ awk -f tst.awk file
{
    "Balance_sheet for AAPL": {
        "Treasury Shares Number": {
            "2023-09-30": "0.0",
            "2022-09-30": "NaN",
            "2021-09-30": "NaN",
            "2020-09-30": "NaN"
        },
        "Ordinary Shares Number": {
            "2023-09-30": "15550061000.0",
            "2022-09-30": "15943425000.0",
            "2021-09-30": "16426786000.0",
            "2020-09-30": "16976763000.0"
        }
    }
}

複数の「貸借対照表」ブロックを処理する必要がある場合は、以下を追加します。

if ( numTables++ ) {
    printf "\n%*s},\n", indent, ""
}
numDates = numItems = 0

sub()たとえば、次の入力が与えられた場合は、この行のすぐ下にあります。

$ cat file2
Balance_sheet for AAPL:

                                                        2023-09-30      2022-09-30      2021-09-30      2020-09-30
Treasury Shares Number                                         0.0             NaN             NaN             NaN
Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0

Balance_sheet for foo:

                                                        2023-09-30      2022-09-30      2021-09-30      2020-09-30
Treasury Shares Number                                         0.0             NaN             NaN             NaN
Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0

このスクリプトは次のとおりです。

$ cat tst.awk
BEGIN {
    inStep = 4
    print "{"
}

sub(/:$/,"") {
    if ( numTables++ ) {
        printf "\n%*s},\n", indent, ""
    }
    numDates = numItems = 0
    indent = inStep
    printf "%*s\"%s\": {\n", indent, "", $0
    next
}

!numDates && /^[[:space:]]/ {
    numDates = split($0,dates)
    next
}

numDates && match($0,"[[:space:]]+([^[:space:]]+[[:space:]]*){"numDates"}$") {
    indent += inStep
    printf "%s%*s\"%s\": {\n", (numItems++ ? ",\n" : ""), indent, "", substr($0,1,RSTART-1)

    indent += inStep
    $0 = substr($0,RSTART,RLENGTH)
    for ( i=1; i<=numDates; i++ ) {
        printf "%*s\"%s\": \"%s\"%s\n", indent, "", dates[i], $i, (i<numDates ? "," : "")
    }
    indent -= inStep

    printf "%*s}", indent, ""
    indent -= inStep
}

END {
    printf "\n%*s}\n", indent, ""
    print "}"
}

次の出力が生成されます。

$ awk -f tst.awk file2
{
    "Balance_sheet for AAPL": {
        "Treasury Shares Number": {
            "2023-09-30": "0.0",
            "2022-09-30": "NaN",
            "2021-09-30": "NaN",
            "2020-09-30": "NaN"
        },
        "Ordinary Shares Number": {
            "2023-09-30": "15550061000.0",
            "2022-09-30": "15943425000.0",
            "2021-09-30": "16426786000.0",
            "2020-09-30": "16976763000.0"
        }
    },
    "Balance_sheet for foo": {
        "Treasury Shares Number": {
            "2023-09-30": "0.0",
            "2022-09-30": "NaN",
            "2021-09-30": "NaN",
            "2020-09-30": "NaN"
        },
        "Ordinary Shares Number": {
            "2023-09-30": "15550061000.0",
            "2022-09-30": "15943425000.0",
            "2021-09-30": "16426786000.0",
            "2020-09-30": "16976763000.0"
        }
    }
}

答え3

使用幸せ(以前のPerl_6)

~$ raku -MJSON::Fast -e '
         my $a = lines[0..1].trim-trailing;         \
         my @a = slurp.map("Date" ~ *).lines.map:   \
             *.subst(:global, / <alpha>+ % " " /, { .trans(" " => "_") } ).split(/ \s+ /);  \
         @a = [Z] @a; my %h; for 1..^@a[0].elems -> $j {   \
             %h.append: @a.[0][$j] => %(@a.map( { $_.[0] => $_.[$j] } )[1..*]) };  \ 
         put to-json( $a => %h, :sorted-keys );'   file

上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。 OPによって発行された「貸借対照表」テストファイルが制限されていることを考慮すると、「前処理」テーブルもそれに応じて制限され、カスタマイズできます。したがって、データを標準形式に整理するために使用される最初の数行のコードは、すべての「貸借対照表」を処理する方法を表すのではなく、Raku言語の「味」を提供すると解釈されるべきです。

  1. RakuモジュールはJSON::Fastコマンドラインにロードされます。
  2. 最初のドアは最初の2行をスカラーとして$a読み込み、trim-mingは末尾のスペースを削除します。
  3. 2番目の声明 )。slurpファイルの残りの部分をメモリに保存し、 第二)。プレフィックス"Date"文字列、 )。すべてをlinesD)。それぞれは次のように入力さlineれます。map金利)。スペースにアンダースコアが付けられるように、単一のsubst文字スペースで区切られた文字パターンをデザインして、行ラベルからスペースを削除します。それではついに% " "trans_F)。データは残りの\s+スペースに分割され、現在の長方形のテーブルは配列@aに格納されます。
  4. 行が列になり、その逆にテーブルが@a[Z]zip」に変換されます。長方形のデータ以外の項目にこの演算子を使用する場合は注意してください。
  5. %hハッシュ値が宣言されます。
  6. 「共有」データ列の数を繰り返します。)。%()キーと値のペアを含む匿名ハッシュを生成します。各ペアには、次の日付(行ラベル)があります。対応する「共有」データ列の場合(ここでインデックスを使用すると、重複した[1..*]「タグ」ペアが削除されます)。
  7. 別のレベルを追加し、第二)。適切な「共有」(つまり、列)ラベルは次のとおりです。匿名(日付/株式)ハッシュの場合、%()2番目のレベルになります。。キー/値に変換された「日付/共有」列はハッシュ値appendとして編集されます。%h
  8. 最後に(本当に良いです)この時点で、$a「Balance_Sheet」ヘッダーは次のように再追加されます。%hハッシュするそして、この(これはマルチレベル)最終ハッシュテーブルが変換され、名前付きパラメータが追加されてto-json()出力:sorted-keysされますput

入力例:

Balance_sheet for AAPL:

                                                        2023-09-30      2022-09-30      2021-09-30      2020-09-30
Treasury Shares Number                                         0.0             NaN             NaN             NaN
Ordinary Shares Number                               15550061000.0   15943425000.0   16426786000.0   16976763000.0

変換されたサンプル入力シート[Z](「Balance_Sheet」ヘッダ行を除く):

(Date Treasury_Shares_Number Ordinary_Shares_Number)
(2023-09-30 0.0 15550061000.0)
(2022-09-30 NaN 15943425000.0)
(2021-09-30 NaN 16426786000.0)
(2020-09-30 NaN 16976763000.0)

最終JSON出力:

{
  "Balance_sheet for AAPL:": {
    "Ordinary_Shares_Number": {
      "2020-09-30": "16976763000.0",
      "2021-09-30": "16426786000.0",
      "2022-09-30": "15943425000.0",
      "2023-09-30": "15550061000.0"
    },
    "Treasury_Shares_Number": {
      "2020-09-30": "NaN",
      "2021-09-30": "NaN",
      "2022-09-30": "NaN",
      "2023-09-30": "0.0"
    }
  }
}

https://raku.land/cpan:TIMOTIMO/JSON::クイック
https://docs.raku.org/言語/hashmap
https://docs.raku.org/
https://raku.org

関連情報