技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

BIツール「Amazon QuickSight」の紹介

実務でBIツール「Amazon QuickSight」の導入を支援する機会がありましたので、ツールの紹介をします。
 
【BIツールとは】
BIとは「ビジネスインテリジェンス」の略であり、事業上の意思決定のために、情報を収集・加工し、分析し、知見を得ることを指します。

ここで言う「情報」とは、売上や費用等の自社の利益に関するデータ、及びそれを左右する競合他社や経営環境に関するデータのことを指します。
データの形式としては、CSVファイルやリレーショナルデータベース等の表形式を思い浮かべるとわかりやすいでしょう。

上記のデータに対し、集計を行い、グラフや表や図の形式に加工することで、「どの地域でどの商品の売れ行きが良いのか」「自社の製品と競合他社の製品の価格差は自社の利益にどのような影響を及ぼすのか」「猛暑の年と冷夏の年で自社製品の売上がどの程度変わるのか」といった有益な知見を得ることができます。
その知見は、「どの分野に集中的に投資すれば良いのか」「自社の製品の価格はどのように決めれば良いのか」「毎年の生産量はどのように決めれば良いのか」といった、経営上の意思決定を行う上で役に立ちます。

BIツールは、上記の活動を支援するための各種ツールのことを指します。
身近な所で言うと、表形式のデータの取り扱いと関数を用いた集計、グラフの表示をサポートする「Microsoft Excel」はBIツールとみなすことができるでしょう。
 
Amazon QuickSightとは】
Amazon QuickSight」(以下「QuickSight」)はBIツールの一種です。
BIツールには様々な種類がありますが、QuickSightは、グラフや表や図を一画面で一覧できる「ダッシュボード」を作成するツールに分類されます。

QuickSightで作成できるダッシュボードは強力なものであり、例えば以下のような機能を備えています。
・表示のドリルダウンとドリルアップ(表示されるデータの粒度の細分化・集約化)
・データのフィルタリング(プルダウンやチェックボックスを用いたデータの抽出)
・マップビジュアル(地図の上にデータの大小を視覚的に表現する機能)

QuickSightはローコードで上記の機能を備えたダッシュボードを作成することができ、アプリケーション開発の専門的な知識が無くとも少ない工数でBIを実現できます。
 
Amazon QuickSightで扱うデータ】
QuickSightはダッシュボード作成を支援するツールですので、その元となるデータは自分で用意する必要があります。

QuickSightは様々な形式のデータに対応しており、例えば以下のような形式のデータに対応しています。
CSV形式やTSV形式等のファイル
・各種リレーショナルデータベース(Amazon RDS、OracleSQL ServerMySQLPostgreSQL等)
Amazon S3(オンラインストレージ)や、S3へのSQLでのクエリをサポートするAmazon Athena

QuickSightでは、SQLの集計関数で実現できる集計機能は一通りサポートしており、集計元のデータと集計後のデータを連動させた形で表示(例えば、各店舗のデータの一覧表と、一覧表上の全店舗の平均売上高を同時に表示)させることが多いので、用意するデータは集計する前のものとし、集計はQuickSight上で行うとスムーズに実装が進みます。
例えば、以下のようなデータを用意すると良いです。

チュートリアル_ 準備完了済みの Amazon QuickSight データセットを作成する - Amazon QuickSight

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/example-prepared-data-set.html

 web-and-social-analytics.csv.zip

 

Amazon QuickSightのデモ】
QuickSightのデモが公式に公開されています。
このデモを操作することで、QuickSightがどのようなツールなのかイメージがつくと思います。

DemoCentral

https://democentral.learnquicksight.online/#Analysis-DashboardDemo-AnyTel-Insights

 

Amazon QuickSightのチュートリアル
QuickSightのチュートリアルが公式に公開されています。

このチュートリアルをこなすことで、QuickSightを使用した簡単なダッシュボード作成を行えるようになります。
チュートリアルで基礎を学んだ後に、高度な機能を調べたり、QuickSightを実際に使用しながら学ぶことで、より難しいダッシュボードも作成できるようになります。

Amazon QuickSight - Visualization Basics (Japanese)

https://catalog.us-east-1.prod.workshops.aws/workshops/aa601d0b-84c9-4f77-b9a7-5954d8574cd5/ja-JP

CDツールを使わない場合のデプロイ手順のイメージ

現在の開発現場・運用現場では、CD(Continuous Delivery)ツールを使用したデプロイ(サーバーへの資材配置)が一般的になっています。
CDツールの設定を行うのは一部の技術者のみであることもあり、CDツールを使用しない場合の原始的な手順でのデプロイ手順は、経験の浅い技術者にはイメージしにくいものであると感じています。
 
そこで、この記事では、CDツールを使用しない場合のデプロイ手順のイメージを書いていきます。
デプロイ手順を知ることで、CDツールを使う理由やそのありがたさを理解しやすくなると思います。
 
----
 
開発した機能を本番サービスに反映させるためには、開発環境で開発を行った資材(プログラムの実行モジュールやスクリプト、設定ファイル等)を商用環境に配置する必要があります。
配置を行うためには、最低限下記の作業が必要になります。
(コマンドのイメージも併記します)
 
1.商用環境のサーバーへのログイン
ターミナルでxxxサーバーにログイン(IPアドレス、ユーザー名、パスワードを入力)

2.ファイル共有サーバーからの資材取得
#> ftp xxx.xxx.xxx.xxx
#> username
#> password
#> get xxx.tar
#> bye

3.資材(圧縮ファイル)の解凍
#> tar xvf xxx.tar

4.解凍後の資材の配置
#> cp -rp xxx yyy
 
上記の作業を行うだけでも、コマンドの誤り・漏れによる作業ミスの可能性がありますし、入るサーバーを間違えてしまうことすらあります。
作業を引き継ぐことにも困難さを伴い、作業手順書のようなものが必要になります。
 
加えて、下記のような作業を伴うことも多く、実際の手順はより複雑になります。
・デプロイ時のサービス停止や機能制限、それに伴う設定変更
 (サーバー毎に設定が異なることも多い)
・デプロイ前のバックアップ取得
・デプロイ後の定型的な確認作業
 
----
 
作業ミスを防いだり引継ぎを容易にしたりする上で、CDツールの利用が有効になります。
CDツールでは、コマンドや設定を登録し、複数のサーバーに対して画面からGUIベースで作業することができるようになります。
 
イメージとしては以下のようなものになります。
(以下は、CircleCIのチュートリアルの一部です)
 
Hello World - CircleCI
https://circleci.com/docs/ja/hello-world/

n対nマッチングのロジック(C#のサンプルコード付き)

以前に、以下の記事にて、マッチング処理のロジックについて書かせていただきました。

マッチング処理のロジック – サイゼントの技術ブログ


以前の記事では1対1マッチングと1対nマッチングについて説明しました。
今回の記事では、より複雑なn対nマッチングについて補足します。


1対1マッチングは、マスタデータの1つのキー項目に対して、トランザクションデータの0~1つのレコードが対応するものでした。
1対nマッチングは、マスタデータの1つのキー項目に対して、トランザクションデータの0~複数のレコードが対応するものでした。
n対nマッチングは、マスタデータ側も1つであるとは限らず、トランザクションデータの1つのキー項目に対して、マスタデータの0~複数のレコードが対応するケースもある、というものを指します。


n対nマッチングでは、以前に参照したトランザクションデータのレコードが、再び参照される可能性があります。
ファイルに対してランダムにアクセスすることでこれを実現できますが、処理が複雑になるため、今回はファイルは順次読み込みのままで、読み込んだトランザクションデータのレコードを一時的に退避するロジックを提示します。


フローチャートと例は以下の通りとなります。


フローチャート

【例】
・要件
商品名が管理されている商品マスタと、商品の販売履歴(トランザクション)をファイル形式で読み込み、商品名と販売日を別ファイルで出力したい。


・商品マスタのフォーマット
カンマ区切りの固定長ファイル。
商品コードと商品副コードでレコードを一意に特定できるようにデータをセットする。

商品コード(7桁)
カンマ(1桁)
商品副コード(2桁)
カンマ(1桁)
商品名(20桁)

・販売履歴のフォーマット
カンマ区切りの固定長ファイル。
商品コード・販売日でレコードを一意に特定できるようにデータをセットする。

商品コード(7桁)
カンマ(1桁)
販売日(8桁)
カンマ(1桁)
販売個数(5桁)
カンマ(1桁)
販売金額(9桁)

・出力ファイルのフォーマット
商品名(20桁)
カンマ(1桁)
販売日(8桁)

・プログラムのフォルダ構成
execute.bat
matching.cs
files┬master.csv
     └transaction.csv

ソースコード(execute.bat)
@echo off

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe matching.cs
matching.exe
del matching.exe

pause

ソースコード(matching.cs)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace Program
{
    class Program
    {
        // EOFフラグ
        static bool isSrmEof = false;
        static bool isSrtEof = false;
        
        static void Main(string args)
        {
            // ファイルオープン
            StreamReader srm = new StreamReader
                (@"files\master.csv", Encoding.UTF8);
            StreamReader srt = new StreamReader
                (@"files\transaction.csv", Encoding.UTF8);
            StreamWriter sw = new StreamWriter
                (@"files\matched.csv", false, Encoding.UTF8);
            
            // 現キー
            string tmpNowKey;
            
            // トランレコード退避用配列
            // COBOLの場合は、十分な長さのOCCURS句を定義する、一時ファイルのOPENとCLOSEを繰り返す、等で対応
            ArrayList tmpTranRecordList;
            
            // 先読みRead
            string
mRecord;
            string tRecord;
            mRecord = mRead(srm);
            tRecord = tRead(srt);
            
            // マッチング処理のループ
            while (!isSrmEof || !isSrtEof)
            {
                // masterのみの場合
                if ((!isSrmEof && isSrtEof) ||
                    (string.Compare(mRecord[0],tRecord[0]) < 0))
                {
                    // 何もしない
                    // master読み込み
                    mRecord = mRead(srm);
                }
                
                // マッチした場合
                else if ((!isSrmEof && !isSrtEof) &&
                         (string.Compare(mRecord[0],tRecord[0]) == 0))
                {
                    // 現キー退避
                    tmpNowKey = mRecord[0];
                    
                    // トランレコード退避用配列初期化
                    tmpTranRecordList = new ArrayList();
                    
                    // transactionが次のキーに進むまでループ
                    while ((!isSrtEof) &&
                           !(string.Compare(tmpNowKey,tRecord[0]) < 0))
                    {
                        // トランレコード退避
                        tmpTranRecordList.Add(tRecord[1]);
                        
                        // transaction読み込み
                        tRecord = tRead(srt);
                    }
                    
                    // masterが次のキーに進むまでループ
                    while ((!isSrmEof) &&
                           !(string.Compare(tmpNowKey,mRecord[0]) < 0))
                    {
                        // 退避したトランレコードを順次結合しファイル出力
                        for (int i = 0; i < tmpTranRecordList.Count; i++)
                        {
                            sw.WriteLine(mRecord[2] + "," + tmpTranRecordList[i]);
                        }
                    
                        // master読み込み
                        mRecord = mRead(srm);
                    }
                }
                
                // transactionのみの場合
                else if ((isSrmEof && !isSrtEof) ||
                         (string.Compare(mRecord[0],tRecord[0]) > 0))
                {
                    // エラー出力
                    Console.WriteLine("Error:" + tRecord[0] + " is tran only.");
                    
                    // transaction読み込み
                    tRecord = tRead(srt);
                }
            }
            
            // ファイルクローズ
            srm.Close();
            srt.Close();
            sw.Close();
        }
        
        // MasterファイルRead
        static string
mRead(StreamReader srm)
        {
            if (srm.Peek() == -1)
            {
                isSrmEof = true;
                return null;
            }
            else
            {
                string str = srm.ReadLine();
                return str.Split(',');
            }
        }
        
        // TransactionファイルRead
        static string[] tRead(StreamReader srt)
        {
            if (srt.Peek() == -1)
            {
                isSrtEof = true;
                return null;
            }
            else
            {
                string str = srt.ReadLine();
                return str.Split(',');
            }
        }
    }
}

・商品マスタのレコード(files\master.csv)
0000001,00,hoge                
0000002,00,fuga                
0000004,01,piyo-Red            
0000004,02,piyo-Blue           
0000005,01,negi-Miku           
0000005,02,negi-Rin            


・販売履歴のレコード(files\transaction.csv)
0000001,20180401,00100,00010000
0000001,20180402,00200,00020000
0000003,20180401,00001,00001000
0000004,20180401,00002,00002000
0000004,20180402,00004,00004000
0000005,20180401,01000,00100000


・バッチ実行結果(標準出力)
Microsoft (R) Visual C# Compiler version 4.8.9032.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

Error:0000003 is tran only.
続行するには何かキーを押してください . . .


・バッチ実行結果(files\matched.csv)
hoge                ,20180401
hoge                ,20180402
piyo-Red            ,20180401
piyo-Red            ,20180402
piyo-Blue           ,20180401
piyo-Blue           ,20180402
negi-Miku           ,20180401
negi-Rin            ,20180401

Note IT業界で求められるソフトスキル集 公開のお知らせ

新しい仕事を進める中で、ソフトスキル(コミュニケーションスキル)を体系化して伝えることの難しさを実感しました。
既存の文書では伝えることが難しいとも感じましたので、私の方でNote(ブログ)を新たに作成し、そこにまとめました。

こちらも参考になると思いますので、ご興味がありましたら是非。
右下のリンクにも追加しています。

https://note.com/ses_softskill/

SpringFrameworkのAOPが動かない→spring-boot-starter-aopを取り込んでないだけかも

表題の通りです。ググっても出てこなかったので備忘として残します。

 

ビルドを通すだけであれば"org.aspectj"のみ取り込めば良いのですが、実際にAOPを動かす(JoinPoint・PointCutの条件を満たした時にAspectクラスのメソッドを実行させる)ためには、"spring-boot-starter-aop"も取り込む必要があります。

TypeScript+Node.jsのHelloWorld

TypeScriptは、JavaScriptを拡張して作られた言語です。
 
JavaScriptを大規模開発で採用する場合、実行時のエラーが頻発することで生産性や品質が悪化する恐れがあります。
JavaScriptのこの問題を、ビルド時のエラー判定を強化することで解決した言語が、TypeScriptです。
ビルド時のエラー判定を行った上でJavaScriptのコードを生成するため、JavaScriptとの互換性も高いです。
 
TypeScript独自の代表的な文法の一つが変数の型指定であり、この文法によりビルド時に型相違のエラー判定を行うことができます。
 
今回の記事は、このTypeScriptを、Node.jsを用いてWindows環境上で試しに動かしてみます。
環境構築の参考になれば幸いです。
「とにかくTypeScriptを動かしてどのような挙動になるのか確認したい」という場合にも参考になると思います。
 
■前提
・OS
 Windows11
 
・実施日
 2022年12月29日
 
・Node.jsのバージョン
 18.12.1
 
■Node.jsインストール手順
1.Node.jsの公式サイトにアクセスする。
 https://nodejs.org/ja/
 
2.「18.12.1 LTS」をクリックする。
 ※バージョンはその時々で変化しますが、個人で試すだけなら推奨版で良いです。
 
3.「node-v18.12.1-x64.msi」を実行する。
 
4.Node.jsのインストール画面が出てくるので、デフォルト設定でインストールする。
 ※「Install」押下後、インストールするか尋ねるポップアップが出たら「はい」を押下
 
■Node.jsコマンドプロンプト起動手順
1.「Node.js command prompt」を探して起動。


 
■TypeScriptインストール手順
1.「Node.js command prompt」を起動する。
 
2.下記コマンドを発行し、インストールを実行。
npm install -g typescript
 
3.インストール処理の進捗状況が表示された後、下記のような表示が出ればOK。
added 1 package, and audited 2 packages in 4s

found 0 vulnerabilities
npm notice
npm notice New major version of npm available! 8.19.2 -> 9.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.2.0
npm notice Run npm install -g npm@9.2.0 to update!
npm notice
 
■実行確認(Hello World)手順
1.「Node.js command prompt」を起動する。
 
2.下記コマンドを発行し、任意のフォルダ(今回は「C:\tmp\」)に移動する。
cd C:\tmp\
 
3.下記コマンドを発行し、「hello.ts」という名前のファイルを作成する。
type nul>hello.ts
 
4.任意のフォルダに作成した「hello.ts」を、エクスプローラ上から開く。
 ※VS Codeサクラエディタ等の任意のエディタにより開く。
 
5.「hello.ts」に下記(プロンプト上に文字列を出力する命令)を入力して保存。
let msg: string = 'Hello World!';
console.log(msg);
 
6.下記コマンドでTypeScriptのインストール先を確認
npm root -g
 
7.下記コマンドでJavaScriptへ変換。「hello.js」が生成される。
node 6で確認したフォルダパス\typescript\bin\tsc hello.ts
 
8.下記コマンドで「hello.js」を実行、「Hello World!」と出力されればOK。
node hello.js
 
■参考
・生成されたhello.jsは以下の通りであり、JavaScriptの文法への置換を確認できます。
var msg = 'Hello World!';
console.log(msg);

ReactのHelloWorld

フロントエンドのSPA(シングルページアプリケーション)向けのJavaScriptのライブラリであるReactを使用して、HelloWorldを書いてみました。
Reactで何かしらの記述の挙動を確認したい時のベースとして使いやすいようにしています。
 
今回は、下準備無しで動かせるように、CDNサービスを使用しています。
インターネット接続していることが前提となりますが、UNPKGで公開されているパッケージをscriptタグ等で指定することで、ローカルにライブラリを落とすことなくライブラリを使用することができるようになります。
 
SPAらしいHelloWorldになるように、内部状態の変化に応じてHelloWorld!の文字が出たり消えたりするような実装を試しています。
 
なお、ブラウザのバージョンが古くても最新のJavaScriptの書き方で動くように、Babelと呼ばれるライブラリも別途取り込んでいます。
 
■前提
・Reactのバージョン
 v17
 
・Babelのバージョン
 v6
 
■実行確認(Hello World)手順
1.任意のフォルダで"hello.html"という名前のファイルを作成する。
2.「hello.html」に下記を入力してUTF-8で保存。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>React - Hello world!</title>
  <script 
    crossorigin
    src="https://unpkg.com/react@17/umd/react.development.js"
  ></script>
  <script 
    crossorigin
    src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
  ></script>
  <script 
    crossorigin
    src="https://unpkg.com/babel-standalone@6/babel.min.js"
  ></script>
</head>
<body>
  <div id="helloContainer"></div>
  <script type="text/babel">
  
    // useStateの取り込み(CDNサービス使用時の宣言)
    const { useState } = React;
  
    const Hello = () => {
      // 状態管理する変数の宣言
      // const [状態変数, 状態を変更するための関数] = useState(状態の初期値);
      const [checkedValues, setCheckedValues] = useState("");
  
      // チェックボックスの状態が変化した場合の処理
      // e.target.valueは状態が変化したチェックボックスが持つ値
      // (今回はボックスを1つしか定義しないため判定に使用する必要はないが、
      //  複数定義する場合はどのボックスの状態が変化したかを判定するのに必要)
      // checkedValuesが初期値なら"Hello World!"をセット、そうでなければ初期値
      const handleChange = (e) => {
        if (e.target.value === "HelloValue") {
          if (checkedValues === "") {
            setCheckedValues("Hello World!");
          } else {
            setCheckedValues("");
          }
        }
      };
      return (
        <label>
          {/* チェックボックスの定義 */}
          <input
            type="checkbox"
            value="HelloValue"
            onChange={handleChange}
          />
          {/* checkedValuesの値を表示 */}
          {checkedValues}
        </label>
      )
    };
    
    const domContainer = document.querySelector("#helloContainer");
    ReactDOM.render(<Hello />, domContainer);
</script>
</body>
</html>
 
3.「hello.html」をブラウザから開く。
 ※今回はChromeで開きます。
 
初期状態では以下のように何も表示されませんが

 
チェックボックスをチェックすると「Hello World!」と表示されます。

 
ボタンをクリックしてイベントを飛ばしたり、リロードしたりすることなく、画面で変更された内部状態をリアルタイムに画面上に反映することができています。