Have an amazing solution built in RAD Studio? Let us know. Looking for discounts? Visit our Special Offers page!
DelphiRAD Studio事例

データサイエンティストが実世界のデータ可視化にDelphiを選択する理由

smartmockups_lcuqi4b2

この記事は、データサイエンティストでDelphi開発者のMax Kleiner氏の投稿です。彼は、Maxboxの作者であり、このツールを自身のサイトで「コンパイル済みオブジェクトをベースとしたスクリプトツール」と紹介しています。記事では、RAD Studio / Delphiを用いてWindowsソフトウェアを開発し、HTTPリクエストによって取得したJSONオブジェクトを使って、集積ガス貯蔵施設の貯蔵量データをグラフやチャートで可視化する方法を説明しています。

貯蔵ガスのデータをAGSIデータセットのタイムラインとして可視化

「データサイエンティストにはさまざまな種類がある。その中で最高なのはデータに取り組む人だ。」

このデータサイエンスチュートリアルでは、AGISと呼ばれるデータストレージとタイムラインでの可視化について説明しています。AGISは、Aggregated Gas Storage Inventoryの略で、集積ガス貯蔵施設の貯蔵量のデータです。データプロバイダーから新しいサービスが発表されたり更新があり、Webサイトにその情報が掲載されるたびに、そのデータを最新に保つことが可能になります。

GIE(Gas Infrastructure Europe)は、Storage MapやStorage Investment Databaseといった関連情報も、https://www.gie.eu/publications/maps/ で提供しています。

データチャート(グラフ)の表示

タイムラインデータを可視化したグラフは、以下のようになります。

データのダウンロードとグラフ作成に使用したコンポーネント

このツールでは、WinHttp.WinHttpRequest、JSONObjects、そしてTEEChartsライブラリを使用して、プロットのロードとテストを行っています。APIキーも必要になるため、以下のサイトからあらかじめ入手しておきます。

https://agsi.gie.eu/account

データが表現するもの

取得するデータは、日ごとのガス貯蔵量を表します。データは毎日、19:30と23:00(中央ヨーロッパ時間)に更新されます。コードの詳細を説明する前に、スクリプトの主要な部分を以下に示します。

plotform:= getForm2(1400, 600, clsilver, 'Sciplot4maXbox');
plotform.icon.loadfromresourcename(hinstance,'ZHISTOGRAM');

HttpResponse:= getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);      
JSON2Plot(plotform, letGenerateJSON2(HttpResponse));

コード概要

コードのメイン部分では、フォームを生成し、API呼び出しとデータのプロットを行っています。

GIEでは、AGSIおよびALSI Transparency Publication Platformsで、API(Application Programming Interface)サービスを提供しています。

APIアクセスを用いれば、ユーザーはAGSIおよびALSIのWebサイトをバイパスして、データを直接かつ継続的に取得できます。Webサイトから各データセットを個別にダウンロードすることなく、必要に応じてデータ抽出、フィルタリング、集計、サブセット生成が可能になります。APIのデータエクスポートフォーマットは、JSONです。以下は、150日分のサブセットデータです。

グラフに描画するデータ

公開されているデータセットは、ACERに提供されているEICコードマッピングテーブルをベースとしています。ストレージデータは、国別、会社別に集計されます。

API呼び出しでは、国、開始日、日数を指定します。

getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);

データセットは、Excel、CSV,JSON形式でもダウンロードできます。このレポートでは、集計ビューを表示していますが、会社ごと、貯蔵施設ごとの個別データセットにもアクセスできます。

データ取得のためのAPI呼び出し

では、API呼び出しの方法から紹介します。

HttpResponse:= getEnergyStreamJSON2(URL_AGSIAPI2,'DE,2022-01-03,150',AGSI_APIKEY);

このコマンドにより、WinHttp.WinHttpRequestを実行します。失敗すると、以下のような例外が複数生成されます。

Exception: WinHttp.WinHttpRequest: The data necessary to complete this operation is not yet available; or you missed a valid key:

AGSIPost: Failed at getting response:403{
  "error": {
    "code": 403,
    "message": "The request is missing a valid API key.",
    "status": "PERMISSION_DENIED"
  }}

面白いのは、例外がJSON形式になっている点です。

Be careful, also, to expose your key as I get from Git: GitGuardian has detected the following Google API Key exposed within your GitHub account.

APIでJSONデータを取得する

Next is the formatting of the get-call with a valid API-key request in the function energyStream()関数で、有効なAPIキーリクエストを使用してGET呼び出しを書式化します。

function getEnergyStreamJSON2(AURL, feedstream, aApikey:string): string;
   ...
   encodURL:= Format(AURL,[HTTPEncode(asp[0]),(asp[1]),asp[2]]);  
   writeln(encodurl)  //debug     
   hr:= httpRq.Open('GET', encodURL, false);
   httpRq.setRequestheader('user-agent',USERAGENTE);
   httpRq.setRequestheader('x-key',aAPIkey);
   ...

ところで、content-typeはどこで設定するのでしょうか。私の知る限り、content-typeを設定する箇所は、Webリクエストの2箇所のみです。

  1. クライアントが、サーバーに送信するbodyに対して、content-typeを設定(つまり、GETまたはPOSTで設定)
  2. サーバーが、レスポンスに対してcontent-typeを設定

利用可能なContent-Typeを明示する

ペイロードボディを含むメッセージを生成する送信元は、そのcontent-typeが不明でない限り、Content-Typeヘッダフィールドを生成する必要があります。それ以外の場合、503503 - Service Unavailable という応答で取得に失敗します。

('headers={"Content-Type":"application/json"}')
  httpRq.setRequestheader('Content-Type',application/json);

つまり、content-type HTTPヘッダは、PUTおよびPOSTでのみ設定する必要があるということです。GETリクエストでは、クライアントが理解できるコンテンツタイプを示す「Accept」ヘッダを含めることができます。サーバーは、この情報を用いて、返信するコンテンツ タイプを決定できます。

オプションで、TALWinInetHttpClientを用いることもできます。これは、使いやすいWinInetベースのプロトコルで、HTTPSをサポートしています。このHTTPクライアントコンポーネントは、HTTPプロトコルを介してWebでデータの送信および取得ができます。

function TALHTTPClient_Get(aUrl: AnsiString;
                           feedstream, aApikey: string): string;

Var
  LHttpClient: TALWininetHttpClient;
  asp: TStringArray;
begin
  LHttpClient:= TALWininetHttpClient.create;
  asp:= splitStr(feedstream,',');
  LHttpClient.url:= Format(AURL,[HTTPEncode(asp[0]),(asp[1]),asp[2]]);
  LHttpClient.RequestMethod:= HTTPmt_Get; //HTTPrm_Post;
  LHttpClient.RequestHeader.UserAgent:=USERAGENTE;
  //LHttpClient.RequestHeader.CustomHeaders:=
  LHttpClient.RequestHeader.RawHeaderText:='x-key:'+aAPIkey;
  try
    result:= LHttpClient.Get1(LHttpClient.url); //overload;   
  finally
    LHttpClient.Free;
  end;
end;

JSONレスポンスをグラフで使用できるデータに変換する

AGSIでは、欠落したデータも不完全なデータも表示されます。次に行うのは、取得したJSONデータをTJSONObjectを使って、プロット用に変換することです。

function letGenerateJSON2(HttpRqresponseText: string): TJSONArray;
var jo: TJSONObject; 
begin
  jo:= TJSONObject.Create4(HttpRqresponseText);
  try
    //writeln(jo.getstring('data'));
    writeln(itoa(jo.getjsonarray('data').getjsonobject(0).length))
    writeln(itoa(jo.getjsonarray('data').length))
    result:= jo.getjsonarray('data');

    //write out to check    
    for it:= 0 to result.length-1 do
       writeln(result.getjsonobject(it).getstring('gasDayStart')+':'+
                      result.getjsonobject(it).getstring('injection'));
  except
    writeln('EJson: '+ExceptiontoString(exceptiontype, exceptionparam));
  end;                         
end;

関数の戻り値として取得できるこのJSON配列を、次のプロット処理に渡します。

procedure JSON2Plot(form1: TForm; jar: TJSONArray);
var chart1: TChart; 
    cnt: integer; sumup,tmp2,tmp: double; gday: string;
begin
  form1.onclose:= @Form_CloseClick;
  chart1:= ChartInjector(form1);
  sumup:=0; tmp2:=0; tmp:=0;
  try
   for cnt:= 0 to jar.length-1 do begin
     //writeln(locate.getjsonobject(it).getstring('gasDayStart')+':'+
     tmp:= jar.getjsonobject(jar.length-1-cnt).getdouble('injection');
     tmp2:= jar.getjsonobject(jar.length-1-cnt).getdouble('full');
     sumup:= sumup+tmp;
     gday:= jar.getjsonobject(jar.length-1-cnt).getstring('gasDayStart');
     chart1.Series[0].Addxy(cnt,tmp,gday,clgreen);
     chart1.Series[1].Addxy(cnt,tmp2,'',clred);
     chart1.Series[2].Addxy(cnt,jar.getjsonobject(jar.length-1-cnt).getdouble('withdrawal'),'',clyellow);
   end;
  except
    writeln('EPlot: '+ExceptiontoString(exceptiontype, exceptionparam));
  end;  
 PrintF('Landrange %d: Injection sum: %.2f',[jar.length-1,sumup]);
end;

グラフの系列に含まれるデータ

グラフには、4つのデータ系列をプロットします(タイムラインを含む)。

  1. 注入量(ガス日あたり)
  2. 充足率 – Storage / WGV(in%)
  3. 放出量(ガス日あたり – 2桁精度)
  4. ガス日(レポートの開始ガス日)

時系列は、ガス日単位です。

「ガス日」とは、 UTCで5:00~5:00(夏時間では、4:00~4:00)を意味します。ガス日は、ヨーロッパ中央時間(CET)の場合、UTC+1、夏時間(CEST)の場合UTC+2となります(定義: CAM Network Code Specificationsを参照)。

データ取得用APIにアクセスする方法

APIアクセスは、RESTライクなインターフェイス (Representational State Transfer) で提供され、上述したレスポンスヘッダにcontent-typeを含む、JSON形式でデータベースリソースを公開しています。

他言語向けのデータアクセス方法

データサイエンスビジョンのコードには、使用例を示すサンプルが添付されており、Python3、Delphi、Jupyter-Notebook、Object Pascal、maXbox4で使用できます。なお、このAPIサービスは無料公開されています。このプラットフォーム上で現在利用可能なデータのみにアクセスできます。

メモ:システムから直接データを抽出するには、ブラウザで以下のリンクを直接クリックしてください。

https://agsi.gie.eu/api?type=eu

GIEデータAPIに関する情報

GIEでは、AGSIおよびAGSI+ストレージデータに関するAPI(Application Programming Interface)サービスを提供しています。APIドキュメントの作成は進行中であり、サービスの使用方法に関するサンプルやガイダンスも提供しており、APIキーを取得するための登録を行うと利用できます。以下は、過去半年のデータプロットです。

サンプルスクリプト/コードの入手先

スクリプトと画像は、https://github.com/maxkleiner/agsi-dataからダウンロードできます。

ダウンロードコードのサンプル

以下は、maXbox4統合用のWinAPIDownloadクラスからの抜粋です。

TWinApiDownload = class(TObject)
  private
    fEventWorkStart: TEventWorkStart;
    fEventWork: TEventWork;
    fEventWorkEnd: TEventWorkEnd;
    fEventError: TEventError;
    fURL: string;
    fUserAgent: string;
    fStop: Boolean;
    fActive: Boolean;
    fCachingEnabled: Boolean;
    fProgressUpdateInterval: Cardinal;
    function GetIsActive: Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function CheckURL(aURL: string): Integer;
    function Download(Stream: TStream): Integer; overload;
    function Download(var res: string): Integer; overload;
    function ErrorCodeToMessageString(aErrorCode: Integer): string;
    procedure Stop;
    procedure Clear;
    property UserAgent: string read fUserAgent write fUserAgent;
    property URL: string read fURL write fURL;
    property DownloadActive:Boolean read GetIsActive;
    property CachingEnabled:Boolean read fCachingEnabled write fCachingEnabled;
    property UpdateInterval:Cardinal read fProgressUpdateInterval
                                     write fProgressUpdateInterval;
    property OnWorkStart: TEventWorkStart read fEventWorkStart
                                           write fEventWorkStart;
    property OnWork: TEventWork read fEventWork write fEventWork;
    property OnWorkEnd: TEventWorkEnd read fEventWorkEnd write fEventWorkEnd;
    property OnError: TEventError read fEventError write fEventError;
  end;

参考文献とリンク

RAD Studio 13.2 Florence Now Available! Kai 1.1.1 Now Available! What's Coming in RAD Studio 13.2 Florence

Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.

Start Free Trial   Upgrade Today

   Free Delphi Community Edition   Free C++Builder Community Edition

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES