すべてのプロダクト
Search
ドキュメントセンター

PolarDB:PolarDB ベースのグラフ分析: 銀行および金融におけるグラフ分析

最終更新日:Jun 06, 2025

このトピックでは、PolarDB ベースのグラフ分析について説明し、金融取引詐欺検出シナリオを使用して、不正な取引間の相関関係を発見し、取引間の Jaccard 類似度を計算して、詐欺警告を提供します。

グラフデータベースエンジンについて

グラフ分析は、データサイエンスの重要な分野です。グラフ構造を使用してデータを表し、さまざまな計算および分析タスクを実行します。 グラフ構造は、ノード(または頂点)とエッジで構成され、ノードはエンティティを表し、エッジはエンティティ間の関係を表します。グラフコンピューティングは、ソーシャルネットワーク分析、レコメンデーションシステム、知識グラフ、パス最適化などの分野で一般的に使用されます。

PolarDB for PostgreSQL は Apache AGE と高度に互換性があり、知識グラフの保存とクエリをサポートしています。 標準の ANSI SQL と openCypher グラフクエリ言語の両方を使用して、同じデータベースクラスタ内のデータをクエリできます。

  • PolarDB for PostgreSQL と完全な互換性

    AGE は PolarDB for PostgreSQL の拡張機能です。データベースの再構築を必要とせずに、既存の PolarDB データベースで使用できます。 AGE は、トランザクションサポート、同時実行制御、さまざまなインデックス作成およびパフォーマンス最適化機能など、PolarDB が提供するすべての機能を活用します。

  • 統合されたグラフとリレーショナルクエリ

    AGE は、リレーショナルデータとグラフデータの同時処理を可能にします。単一のクエリで SQL とグラフクエリ言語の両方を使用できるため、複雑なデータモデルの処理が簡素化され、操作効率が向上します。

  • Cypher クエリ言語のサポート

    AGE は、グラフデータベース用に調整された Cypher クエリ言語をサポートしています。その構文はシンプルで柔軟性があり、グラフデータのクエリと操作に対する直感的なアプローチを提供します。

  • 高性能

    PolarDB が提供する最適化機能とグラフデータ用に調整されたインデックスを組み合わせることで、AGE は大規模なグラフデータと複雑なグラフクエリを効率的に管理できます。

結論として、AGE が提供する機能に基づいて、PolarDB はシンプルで効率的な方法でグラフクエリを処理できます。

シナリオ

背景情報

今日の詐欺や金融犯罪では、詐欺師は身元を変更するなどの方法を使用して、リスク管理ルールを回避することがよくあります。グラフ構造は、グラフデータベースを使用してユーザーの行動を追跡し、個別の詐欺データをリアルタイムで分析し、詐欺行為を特定して効果的に防止できます。

データとモデル

データは、金融取引の公開データセットからのものです。これらは Vesta の取引記録であり、デバイス、取引アドレス、メールアドレス、および取引に関連するその他の情報が含まれています。これらのデータセットは、不正な取引を特定し、リスクを特定するために使用できます。

生データは CSV 形式であり、取引情報(取引 ID、取引アドレス、メールアドレスなど)と取引識別情報(デバイス情報やデバイスタイプなど)が含まれています。データセットには大量の情報が含まれています。次の図は、データモデルの概要を示しています。実際のシナリオでは、詳細が異なる場合があります。

  • 頂点

    • transaction

    • product

    • addr1 (取引アドレス 1)

    • addr2 (取引アドレス 2)

    • emaildomain (取引で使用されるメールドメイン名)

    • deviceinfo (取引デバイス情報)

    • devicetype (取引デバイスタイプ)

  • エッジ

    • transaction_product (取引とプロダクトの関係)

    • transaction_addr1 (取引とアドレス 1 の関係)

    • transaction_addr2 (取引とアドレス 2 の関係)

    • transaction_emaildomain (取引とメールドメイン名の関係)

    • transaction_deviceinfo (取引とデバイス情報との関係)

    • transaction_deviceinfo (取引とデバイスタイプとの関係)

上記のモデルでは、トランザクションが中心にあり、トランザクション ID (transactionid) を使用して他のものと関連付けられています。

ベストプラクティス

説明

Age Viewer は、グラフデータベース用の可視化ツールであり、クエリ結果をグラフで表示できます。詳細については、「Age Viewer」をご参照ください。

推奨構成

PolarDB クラスタでは、良好なエクスペリエンスを得るために、次の構成をお勧めします。

  • プロダクト: Standard Edition

  • データベースエンジン: PostgreSQL 14

  • リビジョンバージョン: >=14.12.23.1

  • CPU コア: >= 4

  • メモリ: >= 16 GB

  • ディスク容量: >= 100 GB

データベースの準備

  1. 拡張機能をインストールするには、特権アカウントを使用する必要があります。

    CREATE EXTENSION age;
  2. 拡張機能を検索パスに追加し、拡張機能を使用するデータベースまたはユーザーのライブラリをプリロードします。

    • セッションレベルで拡張機能を読み込みます。

      説明

      [データ管理 (DMS)] を使用して search_path を構成すると、互換性の問題が発生する可能性があります。そのような場合は、PolarDB-Tools を使用して関連するステートメントを実行できます。

      SET search_path = ag_catalog, "$user", public;
      SELECT * FROM get_cypher_keywords() limit 0;
    • 拡張機能を永続的にロードします。

      ALTER DATABASE <dbname> SET search_path = ag_catalog, "$user", public;
      ALTER DATABASE <dbname> SET session_preload_libraries TO 'age';

データベースへのデータの書き込み

  1. グラフを作成します。 ag_catalog スキーマの create_graph 関数を使用して、グラフ(fraud_graph という名前)を作成します。

    SELECT create_graph('fraud_graph');
  2. 頂点とエッジを挿入します。データは CSV ファイルでダウンロードされ、頂点とエッジのグラフデータとして直接保存することはできません。お問い合わせ いただくことで、PolarDB でデータを頂点とエッジに変換するための Python スクリプトを入手できます。変換結果は、以下のセクションに示されています。

    • 頂点データのサンプル

      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:transaction {transactionid : 2990783, isfraud : 0 } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:product {productid : 158945 } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:addr1 {addr1 : '299.0' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:addr2 {addr2 : '87.0' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:emaildomain {emaildomain : 'gmail.com' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:deviceinfo {deviceinfo : 'SM-G920V Build/NRD90M' } ) RETURN v $$ ) as (n agtype);
      SELECT * FROM cypher('fraud_graph', $$ MERGE (v:devicetype {devicetype : 'mobile' } ) RETURN v $$ ) as (n agtype);
      ...
    • エッジデータのサンプル

      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:product) WHERE a.transactionid = 2990783 AND b.productid = 158945 MERGE (a)-[e:transaction_product]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:addr1) WHERE a.transactionid = 2990783 AND b.addr1 = '299.0' MERGE (a)-[e:transaction_addr1]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:addr2) WHERE a.transactionid = 2990783 AND b.addr2 = '87.0' MERGE (a)-[e:transaction_addr2]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:emaildomain) WHERE a.transactionid = 2990783 AND b.emaildomain = 'gmail.com' MERGE (a)-[e:transaction_emaildomain_p]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:deviceinfo) WHERE a.transactionid = 2999403 AND b.deviceinfo = 'SM-G920V Build/NRD90M' MERGE (a)-[e:transaction_deviceinfo]->(b) RETURN e$$) as (e agtype);
      SELECT * FROM cypher('fraud_graph', $$ MATCH (a:transaction), (b:devicetype) WHERE a.transactionid = 2999404 AND b.devicetype = 'mobile' MERGE (a)-[e:transaction_devicetype]->(b) RETURN e$$) as (e agtype);
      ...

    変換結果を SQL ファイルとして保存し、クライアントツール(psql など)を使用してデータをインポートします。

シンプルなクエリ

統計

  • 各タイプの頂点の数をカウントします。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n)
        RETURN count(*)
    $$) as (number_of_vertex agtype);

    サンプル結果:

     number_of_vertex
    ----
     1076004
  • transaction 頂点の数をカウントします。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)
        RETURN count(*)
    $$) as (number_of_transaction agtype);

    サンプル結果:

    number_of_transaction 
    ----
    545591
  • データ内の不正なトランザクションの数をクエリします。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)
        WHERE n.isfraud = 1
        RETURN count(*)
    $$) as (number_of_fraud_transaction agtype);

    サンプル結果:

    number_of_fraud_transaction 
    ----
     18919
  • エッジの数をカウントします。

    SELECT *
    FROM cypher('fraud_graph', $$
        MATCH ()-[r]->()
        RETURN count(*)
    $$) as (number_of_edge agtype);

    サンプル結果:

    number
    ------
     2131254

フィルタークエリとソートクエリ

トランザクション ID 2988706 のトランザクション情報とすべての関連情報をクエリします。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)
    WHERE n.transactionid = 2988706
    RETURN v
$$) as (e agtype);

サンプル結果:

    e
---------
 {"id": 2251799813685249, "label": "addr2", "properties": {"addr2": "87.0"}}::vertex
 {"id": 2533274790395906, "label": "emaildomain", "properties": {"emaildomain": "gmail.com"}}::vertex
 {"id": 2533274790395906, "label": "emaildomain", "properties": {"emaildomain": "gmail.com"}}::vertex
 {"id": 1970324836974595, "label": "addr1", "properties": {"addr1": "325.0"}}::vertex
 {"id": 1125899906844295, "label": "product", "properties": {"productid": 137934}}::vertex

Age Viewer での SQL プレビュー結果:

image

一般的なシナリオ

KNN

k 近傍法 (KNN) は、データノード間の類似性を使用して、潜在的な詐欺行為を特定します。 KNN は、データノード間の類似性を評価するのに役立ちます。ノードが正常かどうかは、データノードと類似の特徴を持つ k 個の近傍を見つけることによって判断できます。たとえば、金額、場所、時間などの特徴の点で、トランザクションがその近傍(トランザクションノード)のほとんどと大きく異なる場合、そのトランザクションは疑わしいとマークされる可能性があります。

トランザクション 2988706 と同じアドレスにあるトランザクションレコード (2 つの最近傍) をクエリします。これらの近傍に関する情報は、このトランザクションが疑わしいかどうかを判断するために使用できます。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[:transaction_addr1]->(:addr1)<-[:transaction_addr1]-(t:transaction)
    WHERE n.transactionid = 2988706
    RETURN t
$$) as (e agtype);

サンプル結果:

  e
-----
 {"id": 844424930131972, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987001}}::vertex
 {"id": 844424930131978, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987007}}::vertex
 {"id": 844424930132041, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987070}}::vertex
 {"id": 844424930132053, "label": "transaction", "properties": {"isfraud": 0, "transactionid": 2987082}}::vertex
....

Age Viewer での SQL プレビュー結果:

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r:transaction_addr1]->(a:addr1)<-[r2:transaction_addr1]-(t:transaction)
    WHERE n.transactionid = 2988706
    RETURN [n,r,a,r2,t]::path
    LIMIT 50
$$) as (e agtype);
説明

50 レコードのみが返されます。レコードが多すぎると、フロントエンドシステムがクラッシュする可能性があります。

image

パスファインディング

不正なトランザクションの特定において、パスは主にグラフ理論とネットワーク分析を使用して潜在的な詐欺行為を特定することに反映されます。金融取引およびネットワークでは、トランザクションは多くの場合グラフとして見ることができ、頂点はアカウントまたは顧客を表し、エッジはアクティビティを表します。このグラフの構造を分析することにより、異常なパターンや詐欺行為を発見できます。トランザクション間のパスを計算することにより、疑わしいアカウントから他のアカウントへのトランザクションチェーンをすばやく特定し、アカウント間の隠れた接続を示すことができます。

トランザクションレコード 2987000 と 2987172 の間の偽のトランザクションのパスをクエリすると、2 つのトランザクションレコードとのすべての偽のトランザクションリンクを見つけることができます。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)<-[r1]-(t:transaction)-[r2]->(v2)<-[r3]-(k:transaction)
    WHERE n.transactionid = 2987000 
        and k.transactionid=2987172
        and t.isfraud = 1
    RETURN t 
$$) as (e agtype);

サンプル結果:

 e
----
 {"id": 844424930618281, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3473312}}::vertex
 {"id": 844424930626886, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3481917}}::vertex
 {"id": 844424930649640, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3504671}}::vertex
 {"id": 844424930631805, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3486836}}::vertex
 {"id": 844424930641980, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3497011}}::vertex
 {"id": 844424930644942, "label": "transaction", "properties": {"isfraud": 1, "transactionid": 3499973}}::vertex

Age Viewer での SQL プレビュー結果:

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[r]->(v)<-[r1]-(t:transaction)-[r2]->(v2)<-[r3]-(k:transaction)
    WHERE n.transactionid = 2987000 
        and k.transactionid=2987172
        and t.isfraud = 1
    RETURN [n,r,v,r1,t,r2,v2,r3,k]::path
    LIMIT 50
$$) as (e agtype);
説明

50 レコードのみが返されます。レコードが多すぎると、フロントエンドシステムがクラッシュする可能性があります。

image

共通近傍判定

共通近傍判定は、ソーシャルネットワークまたはトランザクションネットワーク分析を使用して、起こりうる詐欺行為を特定します。この方法は主にグラフ理論に基づいています。トランザクション参加者間の関係を分析して、疑わしいトランザクションパターンを発見します。2 つのトランザクションに複数の共通の近傍があり、これらの近傍に疑わしい行為(頻繁なトランザクションや異常な金額など)がある場合、2 つのアカウント間のトランザクションは不正である可能性があります。

トランザクションレコード 2987000 と 2987172 の共通の近傍をクエリして、同じ属性(トランザクションアドレスとデバイス)を持つトランザクションを見つけます。

SELECT *
FROM cypher('fraud_graph', $$
    MATCH (n:transaction)-[]->(v)<-[]-(t:transaction)
    WHERE n.transactionid = 2987000 and t.transactionid=2987172
    RETURN v
$$) as (e agtype);

サンプル結果:

  e
-----
 {"id": 2251799813685249, "label": "addr2", "properties": {"addr2": "87.0"}}::vertex
 {"id": 1970324836974594, "label": "addr1", "properties": {"addr1": "315.0"}}::vertex

Age Viewer での SQL プレビュー結果:

image

Jaccard 類似度

Jaccard 類似度法は、次の式を使用して 2 つの集合間の類似度を測定します。

詳細については、「カテゴリー」をご参照ください。

  • J(A,B) は、セット A と B の間の Jaccard 類似度です。

  • ∣A∩B∣ は、セット A と B の積集合です。

  • ∣A∪B∣ は、セット A と B の和集合です。

不正検出では、Jaccard 類似度メソッドは複数の目的に役立ちます。

  • パターン識別: 詐欺行為には、多くの場合、いくつかの共通の特徴があります。 ユーザー トランザクションまたは動作の Jaccard 類似度を計算することで、類似のトランザクション パターンを識別し、潜在的な詐欺行為を検出するのに役立ちます。

  • 顧客グループ分析: 顧客の行動を分析する場合、Jaccard 類似度を使用して、異なる顧客間の類似性を比較できます。 類似度の高い顧客は、同様のリスク特性を持っている可能性があります。

注: この例では、fetch API を使用しています。他の JavaScript ライブラリも使用できます。

  1. 以下の関数を 作成 します。

    • 特定のトランザクションに関連付けられているすべての頂点を取得し、すべての頂点の ID の配列を返す関数。

      CREATE OR REPLACE FUNCTION find_ids(transactionid integer)
      RETURNS bigint[]
      LANGUAGE plpgsql
      AS $function$
      DECLARE 
          sql VARCHAR;
          ids bigint[];
      BEGIN
        sql := 'SELECT array_agg(cast(e as bigint)) FROM (
      SELECT *
      FROM cypher(''fraud_graph'', $$
      MATCH (n:transaction)-[]->(v)
          WHERE n.transactionid = ' || text($1) || 
      'RETURN id(v) 
      $$) as (e agtype)) as t;';
          EXECUTE sql INTO ids;
          return ids;
      END
      $function$;
    • 配列の unionintersection のための 2 つの関数。

      CREATE OR REPLACE FUNCTION array_union(anyarray, anyarray)
      RETURNS anyarray 
      LANGUAGE sql immutable 
      AS $$
      SELECT array_agg(a ORDER BY a)
      FROM (
          SELECT DISTINCT unnest($1 || $2) AS a
      ) s;
      $$;
      
      CREATE OR REPLACE FUNCTION array_intersection(anyarray, anyarray)
      RETURNS anyarray 
      LANGUAGE sql immutable 
      AS $$
      SELECT array_agg(e)
      FROM (
          SELECT unnest($1)
          INTERSECT
          SELECT unnest($2)
      ) AS dt(e)
      $$;
    • 2 つのトランザクション間の Jaccard 類似度を計算するために使用される関数。

      CREATE OR REPLACE FUNCTION jaccardSimilarity(tid1 integer, tid2 integer)
      RETURNS float8
      LANGUAGE plpgsql
      AS $function$
      DECLARE 
          sql VARCHAR;
          ids1 bigint[];
          ids2 bigint[];
          union_list bigint[];
          intersection_list bigint[];
      BEGIN
        ids1 = find_ids($1);
        ids2 = find_ids($2);
        union_list = array_union(ids1, ids2); -- 和集合
        intersection_list = array_intersection(ids1, ids2); -- 共通部分
        RETURN 
          CASE 
              WHEN array_length(union_list,1) = 0 THEN 0
              ELSE array_length(intersection_list,1) * 1.0/ array_length(union_list,1) 
          END AS jaccardSimilarity;
      END
      $function$;
  2. Jaccard 類似度を計算する関数を 実行 します。

    • 指定された 2 つのトランザクション ID 間の類似度を比較します。

      SELECT jaccardSimilarity(2987000, 2987172);

      サンプル結果:

      jaccardsimilarity 
      ----
       0.4
  3. すべてのトランザクション間の類似度を比較するには、PolarDB ストアドプロシージャを使用して、不正なトランザクションを検出するためのより複雑な類似度計算タスクを実行できます。

    たとえば、次の SQL 文を使用して、トランザクション 2987002 と同じ住所 1、住所 2、およびメールドメイン名を持つすべてのトランザクションを検索し、Jaccard 類似度でソートして、類似度の最も高い上位 50 件のトランザクションを取得できます。

    WITH tmp AS
    (SELECT cast(e as integer) as transactionid
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)-[:transaction_addr2]->(:addr2)<-[:transaction_addr2]-(t:transaction)
        WHERE n.transactionid = 2987002
        MATCH (n:transaction)-[:transaction_addr1]->(:addr1)<-[:transaction_addr1]-(t:transaction)
        WHERE n.transactionid = 2987002 
        MATCH (n:transaction)-[:transaction_emaildomain_p]->(:emaildomain)<-[:transaction_emaildomain_p]-(t:transaction)
        WHERE n.transactionid = 2987002 
        RETURN t.transactionid
    $$) as (e agtype) )
    SELECT transactionid, jaccardSimilarity(2987002, transactionid) as jaccardSimilarity
    FROM tmp
    ORDER by jaccardSimilarity DESC
    LIMIT 50;

    サンプル結果:

     transactionid | jaccardsimilarity 
    ---------------+-------------------
           3323911 |               0.6
           3328911 |               0.6
           3009043 |               0.6
           3039416 |               0.6
           3039425 |               0.6
           2993652 |               0.6
           3045027 |               0.6
           3037644 |               0.6
           3045041 |               0.6
    ...

    Age Viewer での SQL プレビュー結果。結果は以前の計算で取得されており、最初の 10 件のトランザクションのみが使用されています。必要に応じて、リストに追加できます。

    SELECT * 
    FROM cypher('fraud_graph', $$
        MATCH (n:transaction)-[r]->(v)<-[r2]-(t:transaction)
        WHERE n.transactionid = 2987002
            AND t.transactionid IN [3323911, 3328911,3009043,3039416,3039425,2993652,3045027,3037644,3045041,3045049,3045279]
        RETURN [n,r,v,r2,t]::path
    $$) as (e agtype);

    image

概要

PolarDB for PostgreSQL はグラフ分析で使用できます。Age Viewer 拡張機能と組み合わせることで、PolarDB を使用して、Cypher クエリ言語を使用したグラフデータの処理とクエリなど、グラフデータの計算と分析を行うことができます。これにより、企業は統一されたデータ管理と分析を実装できます。

試用

PolarDB Free Trial ページにアクセスし、PolarDB for PostgreSQL を選択して GanosBase のグラフコンピューティング機能を試すことができます。