Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr

데이터 과학자가 현실 세계를 효과적으로 시각화하기 위해 델파이를 선택한 이유


Recommended Posts

이안 바커 (Ian Barker)"Why A Data Scientist Chooses Delphi For Powerful Real World Visualizations" 을 번역했습니다. (원문 작성: 2023년 1월, 최종 번역: 2024년 1월)

오늘 소개할 글은 데이터 과학자이자 델파이 개발자인 Max Kleiner가 작성한 글이다. Max는 MaXbox의 개발자이다. MaXbox는 "미리 컴파일된 객체 기반의 스크립팅 도구"라고 Max의 사이트에 소개되어 있다. Max는 자신이 RAD 스튜디오의 델파이를 사용하여 어떻게 윈도우 소프트웨어를 개발했는지 설명한다. 더불어 HTTP 요청을 통해 받은 JSON 오브젝트들을 어떻게 사용해 AGSI 데이터를 그래프와 차트로 시각화 했는지도 설명한다.

(참고: AGSI(Aggregated Gas Storage Inventory, 유럽의 가스 보유량, 반입입, 반출량 등의 데이터를 제공하는 플랫폼)

목차


보유 중인 가스 데이터를 AGSI 데이터셋 타임라인으로 표현

"세상에는 많은 유형의 데이터 과학자들이 있다. 그 중에서 데이터 안으로 깊이 들어가는 사람들이 최고이다."

이 데이터 사이언스 튜토리얼은 이른바 AGSI 데이터 스토리지와 타임라인 시각화에 대해 설명한다. AGSI는 Aggregated Gas Storage Inventory를 줄인 말이다. AGSI 웹사이트에는, 유럽의 가스 재고 데이터 제공 업체의 새로운 서비스 공지 또는 업데이트가 있을 때마다 최신 정보가 게시된다.

GIE(Gas Infrastructure Europe)는 관련 데이터 역시 제공한다. 예를 들면, Storage Map, Storage Investment Database 등을 https://www.gie.eu/publications/maps/에서 제공한다.

이 데이터 차트의 결과는 어떤 모습인가?

이 데이터의 결과는 아래에 있는 차트와 같이 표현된다.

spacer.png

이 데이터를 다운로드 하고 차트로 만들때, 어떤 컴포넌트를 사용했는가?

WinHttp.WinHttpRequest, JSONObjects, TEECharts 라이브러리를 사용해서 이 데이터 관계(원문은 plot임)를 적재하고 테스트 했다.  또한 API 키가 필요하다. 먼저 https://agsi.gie.eu/account로 가서 키를 받도록 한다.

이 앱의 데이터는 무엇을 나타내는가?

가스일(gas day)상 직전 일자의 마지막 가스 보유량 데이터를 표현한다. 데이터 업데이트는 매일 19:30 CET(Central European Time, 중앙유럽표준시)과 23:00 CET에 수행된다. 코드를 더 살펴보기 전에, 이 스크립트의 주요 부분을 먼저 보자:
 

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

코드는 무슨 일을 하는가?

메인(Main)에서는 폼을 하나 생성하고, API을 부르고, 데이터 관를 구성(원문은 plot임)한다.

GIE에서는 API (Application Programming Interface) 서비스를 GIE의 투명성 공개 플랫폼인 AGSI와 ALSI에서 제공하고 있다.

API 접근을 사용하면, AGSI 웹사이트와 ALSI 웹사이트를 열어보지 않아도 해당 데이터를 직접 그리고 지속적으로 확보할 수 있다. 따라서 우리는, 필요에 따라, 데이터를 추출, 필터링, 집계, 하위 집합 생성을 할 수 있다. 각 데이터셋을 웹사이트로부터 따로따로 다운로드할 필요가 없다. API가 내보내는 형식은 JSON이다. 하위 집합인 150일 데이터는 다음과 같이 표현된다:

spacer.png

차트 작성에 사용되는 데이터 안에는 무엇이 있는가?

게시되는 데이터셋들은 ACER에게 제공되는 EIC 코드 맵핑 테이블을 기반으로 한다. 보유량 데이터는 회사별로, 국가별로 집계된다.

호출을 할 때, 국가(country), 시작일(start-date), 일수(number of days)를 전달한다.

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이라는 것이다. 또한, Git에서 받은 당신의 키가 노출되지 않도록 주의하자. 나는 Git으로부터 아래와 같은 메시지를 받은 적이 있다: GitGuardian이 여러분의 GitHub 계정에서 다음과 같은 Google API 키가 노출된 것을 감지했습니다.

API로부터 JSON 데이터을 가져오는 방법 

다음으로, 유효한 API-키 요청을  get 호출에 붙여서 구성하는 방법이다. 아래 energyStream() 함수 안에 구현했다. 

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은 어디에 넣을까? 내가 아는 한, 웹 요청(request)에서 content-type을 설정할 수 있는 곳은 두 곳뿐이다:

  1. 클라이언트는 서버로 보내는 (예를 들어 get 및 post에 대해서) Body에 대한 content-type을 지정한다.
  2. 서버는 응답(response)에 대한 content-type을 지정한다.

사용 가능한 Content-Type 이 있는지 확인하기

동봉된 표현의 미디어 타입(media type)이 unknown이 되도록 의도하는 경우라 아니라면, 생성하는 발신자는 전달하는 본문(payload body)을 담은 메시지의 헤더에 Content-Type 필드를 생성해야 한다. 그렇게 하지 않으면, 응답을 받지 못한다: 503503 - Service Unavailable

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

즉, HTTP 헤더에 content-type이 들어가야 하는 경우는 오직 PUT 과 POST 요청일 때 뿐이다. GET 요청 안에는 “Accept” 헤더를 넣을 수 있다. “Accept” 헤더는 클라이언트가 이해하는 콘텐츠 타입이 무엇인지를 알려준다. 그러면, 서버는 이를 사용해 회신할  content type을 결정할 수 있게 된다.

다른 옵션으로는, TALWinInetHttpClient를 사용하는 방법이 있다. 이것은 WinInet 기반 프로토콜을 사용하기 쉽게 해준다. 또한  HTTPS를 지원한다. HTTP 클라이언트 컴포넌트를 사용하면, 웹에서 HTTP 프로토콜을 통해 어떤 데이터든 보내고 받을 수 있다.

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에서 볼 수 있다. 다음으로, TJSONObject를 사용해, JSON 응답을 데이터 관계(원문은 plot임)를 위해 변환하는 단계로 넘어가자.

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 배열을 위 함수가 반환하면, 우리는 이제 데이터 관계(원문은 plot임)로 넘어간다:

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;

차트 계열 데이터(chart series data) 안에는 무엇이 있는가?

보다시, 4개의 계열이 (타임라인을 포함하여) 준비되었다.

  1. Injection (그 가스일 동안 반입된 량)
  2. Full (보유량/ WGV (in%))
  3. Withdrawal (그 가스일 동안 반입된 양 (2 digits accuracy)).
  4. GasDayStart (리포트 상의 가스일 시작 일자)

시간 계열은 그 가스일(gasday)의 결과이고, 추세가 제공된다.

"가스일(gas day)"은 평소에는 5:00 UTC(Coordinated Universal Time, 협정 세계시)에 시작해 다음날 5:00 UTC에 끝난다. 유럽에 썸머타임이 적용될 때에는 4:00 UTC에 시작해서 다음날 4:00 UTC에 끝난다. 가스 일은 CET(Central European Summer Time, 중앙유럽표준시)로는 UTC+1이고, CEST(Central European Summer Time, 썸머타임이 적용된 중앙유럽표준시)로는 UTC+2이다. (정의: CAM(Capacity Allocation Mechanisms) Network Code 사양 참조

데이터를 얻기 위해 API에 접근하는 방법은?

API 접근은 REST(Representational State Transfer)-같은 인터페이스로 제공된다. 즉 데이터베이스를  JSON 형식으로 노출한다. (위에서 언급한 Response Header 참고)

spacer.png

어디로 가면 다른 개발자들이 이 데이터에 접근 할 수 있는가?

이 데이터 과학 시각화 코드에는 사용 예제가 포함되어 있다. 코드는 파이썬 3, 델파이, 주피터 노트북, 오브젝트 파스칼, maXbox4에서 실행된다. 이 API 서비스는 일반인에게 무료로 제공된다는 점을 알아 두자. 이 플랫폼에서 현재 사용 가능한 데이터만 제공된다.

팁: 시스템에서 직접 데이터를 추출하기 위해서는, 브라우저에서 다음 링크 중 하나를 클릭하면 된다(웹 트래픽과 API 트래픽을 비교해 볼 수 있다):

우리는 GIE의 데이터 API에 대해 무엇을 배웠는가?

GIE에서는 API (Application Programming Interface) 서비스를 GIE의 투명성 공개 플랫폼인 AGSI와 AGSI에서 제공하고 있다. 이 API의 도움말 문서는 작업 중이며, 서비스 사용방법에 대한 예제와 가이드를 제공한다. 등록을 해서 API 키를 발급받은 사람들에게 제공된다. 아래 그림은 지난 반년 동안을 보여준다.

spacer.png

어디로 가면 예제 스크립트와 예제 코드를 받을 수 있는가?

사용된 스크립트와 이미지는 다음 링크에서 찾을 수 있다: https://github.com/maxkleiner/agsi-data 

다운로드 코드를 보여주는 예시가 있는가?

그렇다. WinAPIDownload 클래스는 maXbox4 통합 안에 있는데 아래와 같다.

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; 

참고 문헌 및 부가적인 링크

 

이 글은 기업용 대형 애플리케이션에 대한 기고 경연 대회(Enterprise Article challenge)에 제출된 것이다. 만약 여러분도 델파이, C++빌더 또는 RAD 스튜디오를 사용하여 만든 훌륭한 엔터프라이즈 제품과 프로젝트에 대해 이야기하고 싶은 성공 사례가 있다면 연락을 주기 바란다.

한국 개발자는 데브기어의 델파이 사례 기고 행사에 참여하세요!

이 댓글 링크
다른 사이트에 공유하기

  • 1 month later...

이 토의에 참여하세요

지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.

Guest
이 토픽(기고/질문)에 답하기

×   서식있는 텍스트로 붙여넣기.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   이전에 작성한 콘텐츠가 복원되었습니다..   편집창 비우기

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

중요한 정보

이용약관 개인정보보호정책 이용규칙 이 사이트가 더 잘 작동하기 위해 방문자의 컴퓨터에 쿠키가 배치됩니다. 쿠키 설정 변경에서 원하는 설정을 할 수 있습니다. 변경하지 않으면 쿠키를 허용하는 것으로 이해합니다.