ECサイト購買データを分析!Elasticsearch & Kibana Lens 実践ガイド

BLOG

ECサイトの売上データをもっと活用したいけれど、「SQLでは集計が遅い」「全文検索を使った分析は難しい」「BIツールでは柔軟性に欠ける」と感じたことはありませんか?

そんな課題を一気に解決するのが、Elasticsearch × Kibana Lens です。
本記事では、CSV をドラッグ&ドロップするだけで、高速かつ柔軟に購買データを可視化・分析する手順を、サンプルデータと実例つきでわかりやすく紹介します。

記事のポイント

  • 全文検索 × 集計 × 可視化 を 1 スタックで完結
  • SQL 利用者でも理解できる “発想の違い” を表形式で解説
  • CSV をドラッグ&ドロップするだけで Kibana Lens ダッシュボードを作成
  • 高額購入者セグメント化/都道府県別売上ランキング など具体例付き
  • コード & サンプルデータ一式があり、追って実装が可能

対象読者

  • EC サイトの売上データをもっと活用したいデータアナリスト
  • Elasticsearch は聞いたことあるけれど “全文検索の人” というイメージで止まっている方
  • 従来は SQL+BI ダッシュボードで頑張っているが、レスポンス速度に限界を感じている方

1. SQLと何が違う?Elasticの強みを整理

観点Elasticsearch (+ Kibana)SQL
スケーラビリティシャード&ノード追加で水平分散手動での分割が必要
全文検索形態素解析 (kuromoji) で日本語も高精度LIKE ‘%○○%’ でフルスキャン
集計速度列指向インメモリ計算で TB 級でも数百 ms行指向 RDB は集計件数に比例して遅延
可視化Kibana Lens を同梱(ノーコード)外部 BI ツール必須
リアルタイム性数秒でインデックス反映ETL バッチが前提
「SQL が不要」という意味ではなく、“検索・集計・可視化をワンストップで済ませたい” 場面で優位性があります。

2. 使うデータと分析の目的

randomized_customer_data.csv

年齢・性別・地域・商品・購入金額・注文日を含む合成データ 3,000 行

分析シナリオ

  1. 顧客セグメント(年齢 × 性別 × 地域)
  2. 高額購入者の抽出と特徴把握
  3. 都道府県別売上ランキング
  4. 商品カテゴリ別トレンド

3. インポートと整形

ローカルにインストールしたElasticsearch(本記事ではバージョン8.17.3 を使用)とKibanaを使用し、Pythonは使わずに手動で進めます。

Kibanaの画面上部にある検索バーから「file upload」と入力すると、ファイルアップロード機能が見つかります。Kibanaには多くの機能がありますが、検索画面の利用が最も迅速です。

そこから入ると以下の画面が表示されます

アップできるファイルの大きさにリミットがあるので注意してください。

サンプルデータをドラッグ&ドロップ。

ファイルの中身をさらっと確認できます。「import」をクリックして次の画面に行きます。アップロード可能なファイルサイズには制限があります。サンプルデータをドラッグ&ドロップしてください。「import」をクリックすると、ファイルの内容を簡単に確認し、次の画面に進めます。

インデックス名を決定後に「import」をクリックしても良いですが、今回は「Advanced」タブを選択します。「create data view」に自動的にチェックが入っている点に注意してください。

Advancedタブでは、マッピングやIngest pipelineの変更が可能です。今回は3つのデータを削除したいので、Ingest pipelineのremoveで指定します。自動的に不要と判断されたmessageに加えて、firstnameとlastnameも削除対象に追加します。これらはリスト形式で指定します。

// 変更前:Ingest pipeline

"remove": {
       "field": "message"
     }
// 変更後:Ingest pipeline

     "remove": {
       "field": ["message", "firstname", "lastname"]
     }

資料をインポートする際は、まず「import」をクリックしてください。処理が完了すると、「File processed」から「Data view created」にチェックマークがつきます。

もし処理がうまくいかず❌印が表示されます。最後までチェックマークが付いていても、一部データが読み込めなかったというアラートが出る場合もありますので、再試行を推奨します。最後までチェックマークがついたら、インデックス作成は完了しているため、再インデックスを行う前に既存のインデックスを削除する必要があります。

特に注意が必要なのは、インポート時に「create data view」にチェックを入れていた場合、インデックスだけでなくdata viewの削除も必須となる点です。

作成したインデックスが正しく作成されたか確認するには、左側のメニューから「Managment」>「Stack Management」>「Index Mangment」>「Indices」タブを開き、作成したインデックス名が存在するか確認します(上部の検索バーに「index managment」と入力して検索することも可能です)。

インデックスを削除する場合は、同じIndicesタブから実行できます。

データビューを削除したい場合は、検索バーに「data view」と入力してください。

インデックスを削除するには、左側のチェックボックスにチェックを入れ、青いボタンの「Manage index」から「delete index」を選択してください。

データビューで削除したい項目にチェックを入れ、表示されるピンク色のアイコンを選択して削除を実行します。

データのインポートは完了しました。

4. Dev Tools上のクエリ例:分析テンプレート集

Dev Toolsも上部の検索ボックスから検索してスタートできます。

インデックスのmappingを確認します。

GET ec_site/_mapping
//_mappingのoutput

{
 "ec_site": {
   "mappings": {
     "_meta": {
       "created_by": "file-data-visualizer"
     },
     "properties": {
       "@timestamp": {
         "type": "date"
       },
       "age": {
         "type": "long"
       },
       "area": {
         "type": "keyword"
       },
       "areacode": {
         "type": "long"
       },
       "birthday": {
         "type": "date",
         "format": "iso8601"
       },
       "customerid": {
         "type": "keyword"
       },
       "firstname": {
         "type": "keyword"
       },
       "lastname": {
         "type": "keyword"
       },
       "lastorderdate": {
         "type": "date",
         "format": "iso8601"
       },
       "product": {
         "type": "keyword"
       },
       "sex": {
         "type": "long"
       },
       "totalprice": {
         "type": "long"
       }
     }
   }
 }
}

propertiesにある@timestampはElasticsearchが自動的に追加したもので、その詳細について確認します。

GET ec_site/_search

誕生日フィールドを@timestampとして取ってきているようですが、分析に使わないので、areacodeとセットで落とします。

POST _reindex
{
 "source": {
   "index":"ec_site"
 },
 "dest": {
   "index": "ec_site_v1"
 },
 "script": {
   "lang": "painless",
   "source": """
     ctx._source.remove('@timestamp');
     ctx._source.remove('areacode');
     """
 }
}

再度確認いたします。

GET ec_site_v1/_search
{
 "query": {
   "match_all": {}
 }
}

不要なものが削除されています。

分析例:

// 年齢の分布

GET ec_site_v1/_search
{
 "size":0,
 "aggs":{
   "age_dist":{
     "histogram":{
       "field":"age",
       "interval":10
     }
   }
 }
}
// 性別分布

GET ec_site_v1/_search?filter_path=aggregations     <--- アウトプットがaggregation部分だけを表示させる
{
 "size":0,
 "aggs":{
   "by_gender":{
     "terms":{
       "field": "sex"
     }
   }
 }
}
// 性別ごとの売上合計

GET /ec_site_v1/_search?filter_path=aggregations
{
  "size": 0,
  "runtime_mappings": {
    "sex_label": {				← ラベル用の仮想フィールドを定義
      "type": "keyword",
      "script": {
        "source": """
        if (doc['sex'].size()==0) { emit('不明'); } 
        else if (doc['sex'].value == 1) { emit('男性'); } 
        else if (doc['sex'].value == 2) { emit('女性'); } 
        else { emit('その他'); }"""
      }
    }
  },
  "aggs": {
    "total_sales": {                    <-- ここで全体売上を集計
      "sum": {
        "field": "totalprice"
      }
    },
    "sales_by_sex": {                <-- 性別ごとの売上集計   
      "terms": {
        "field": "sex_label",
        "order": { "_key": "asc" }
      },
      "aggs": {
        "sex_sales": {
          "sum": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// カスタマー分析:高額購入者の傾向

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "high_value_customers": {
      "filter": {
        "range": {
          "totalprice": {
            "gt": 70000
          }
        }
      },
      "aggs": {
        "age_distribution": {
          "histogram": {
            "field": "age",
            "interval": 10
          }
        },
        "top_areas": {
          "terms": {
            "field": "area.keyword",
            "size": 3
          }
        },
        "gender_ratio": {
          "terms": {
            "field": "sex"
          }
        }
      }
    }
  }
}
// 性別ごとの商品カテゴリー分析

GET /ec_site_v1/_search?filter_path=aggregations
{
  "size": 0,
  "aggs": {
    "gender": {
      "terms": {
        "field": "sex",
        "size": 3
      },
      "aggs": {
        "product_categories": {
          "terms": {
            "field": "product.keyword",
            "size": 10
          },
          "aggs": {
            "total_sales": {
              "sum": {
                "field": "totalprice"
              }
            },
            "avg_purchase": {
              "avg": {
                "field": "totalprice"
              }
            }
          }
        }
      }
    }
  }
}

productを指定のデータのみで集計したい場合、主に二つの方法が考えられます。

GET /ec_site_v1/_search?filter_path=aggregations
{
  "size": 0,
  "query": {
    "term": {
      "product.keyword": "カーテン"
    }
  },
  "aggs": {
    "gender": {
      "terms": {
        "field": "sex",
        "size": 3
      },
      "aggs": {
        "total_sales": {
          "sum": { "field": "totalprice" }
        },
        "avg_purchase": {
          "avg": { "field": "totalprice" }
        }
      }
    }
  }
}

GET /ec_site_v1/_search?filter_path=aggregations
{
  "size": 0,
  "aggs": {
    "curtain_only": {                          
      "filter": {
        "term": { "product.keyword": "カーテン" }
      },
      "aggs": {
        "gender": {
          "terms": {
            "field": "sex",
            "size": 3
          },
          "aggs": {
            "total_sales": {
              "sum": { "field": "totalprice" }
            },
            "avg_purchase": {
              "avg": { "field": "totalprice" }
            }
          }
        }
      }
    }
  }
}
//商品の絞り込みと性別フィルターを同時にかけたい場合

GET /ec_site_v1/_search?filter_path=aggregations
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "product.keyword": "カーテン"
          }
        },
        {
          "term": {
            "sex": 2
          }
        }
      ]
    }
  },
  "aggs": {
    "gender": {
      "terms": {
        "field": "sex",
        "size": 3
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "totalprice"
          }
        },
        "avg_purchase": {
          "avg": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// 年齢層による購買分析

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "age_ranges": {
      "range": {
        "field": "age",
        "ranges": [
          { "to": 30, "key": "30歳未満" },
          { "from": 30, "to": 50, "key": "30-49歳" },
          { "from": 50, "key": "50歳以上" }
        ]
      },
      "aggs": {
        "products": {
          "terms": {
            "field": "product.keyword",
            "size": 5
          },
          "aggs": {
            "total_sales": {
              "sum": {
                "field": "totalprice"
              }
            }
          }
        },
        "avg_purchase": {
          "avg": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// 商品種類ごとの平均購入金額

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "products": {
      "terms": {
        "field": "product.keyword",
        "size": 10
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
# リピート顧客分析 (重複ID)

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "unique_customers": {
      "cardinality": {
        "field": "customerid.keyword"
      }
    },
    "total_orders": {
      "value_count": {
        "field": "customerid.keyword"
      }
    }
  }
}
# 上位5都道府県の累計売上

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "top_5_areas": {
      "terms": {
        "field": "area.keyword",
        "size": 5,
        "order": {
          "area_sales": "desc"
        }
      },
      "aggs": {
        "area_sales": {
          "sum": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// 県ごとの売上比較

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "region_comparison": {
      "terms": {
        "field": "area.keyword",
        "size": 47
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "totalprice"
          }
        },
        "order_count": {
          "value_count": {
            "field": "customerid.keyword"
          }
        }
      }
    }
  }
}
// 総支出額が最も高いお客様の特定

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "high_value_customers": {
      "terms": {
        "field": "customerid.keyword",
        "size": 10,
        "order": {
          "total_spend": "desc"
        }
      },
      "aggs": {
        "total_spend": {
          "sum": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// 合計価格によるトップ製品

GET /ec_site_v1/_search
{
  "size": 0,
  "aggs": {
    "top_products": {
      "terms": {
        "field": "product.keyword",
        "size": 10,
        "order": {
          "product_sales": "desc"
        }
      },
      "aggs": {
        "product_sales": {
          "sum": {
            "field": "totalprice"
          }
        }
      }
    }
  }
}
// 購入額 totalprice の1〜99パーセンタイルを取得し、顧客セグメントのしきい値を一目で把握

GET ec_site_v1/_search
{
 "size":0,
 "aggs":{
   "spending_distribution":{
     "percentiles":{
       "field": "totalprice",
       "percents":[1,25,50,75,90,95,99]
     }
   }
 }
}
// 購入額が高い順に上位10件の顧客ID・購入額・年齢・性別を取得

GET ec_site_v1/_search
{
 "size":10,
 "sort":[
   {"totalprice":"desc"}
 ],
 "_source": ["customerid","totalprice","age","sex"]
}

5. Kibana Lensでの可視化

Kibana Lensは、ドラッグアンドドロップの簡単な操作で直感的にデータを可視化できるツールです。フィールドの管理、迅速な集計メトリクス、チャートタイプの即時切り替えが可能で、データの検索、フィルタリング、ダッシュボード作成まで柔軟に対応できます。高い技術的スキルがなくても、手軽に高度なデータ分析環境を構築できるのがKibana Lensの魅力です。

Kibana の Lens や Discover では、インデックスそのものではなく Data View を通じてデータにアクセスします。ですので_reindexを使った後、新しいdata viewの登録が必要です。

  • 旧インデックス:ec_site の Data View が存在
  • 新インデックス:ec_site_v1 は 新しい名前のインデックス なので、既存の Data View では見えません

Kibana Lensは、ドラッグアンドドロップ操作で直感的なデータ可視化を実現するツールです。高度な技術がなくても、フィールド管理、迅速な集計、チャートタイプ切り替えにより、容易にデータ分析環境を構築できます。データの検索、フィルタリング、ダッシュボード作成にも柔軟に対応可能です。

KibanaのLensやDiscoverでは、インデックスではなくData Viewを介してデータにアクセスするため、_reindex後に新しいData Viewの登録が必要です。例えば、旧インデックス「ec_site」のData Viewが存在する場合でも、新インデックス「ec_site_v1」は新しい名前のインデックスであるため、既存のData Viewからは参照できません。

Kibanaで、上部の検索ボックスに「lens」と入力してください。

Lensを選択して入ると下の画面が見えます。

基本的な使い方は左側に表示されているデータのフィールドをドラッグ&ドロップするだけで簡単にチャートを作成することができます。

基本的な操作方法

データビューの選択:

ドラップダウンメニューから可視化したいデータビューを選択します。今回、データのアップロード時にビューを使っているのでありますがない場合create a data viewから作成できます。またruntime fieldsを作成したい時にAdd a field to this data viewからできます。

フィールドの選択

左側のパネルには、利用可能なフィールド(例:「年齢」「性別」「商品名」など)が表示されます。目的に合わせて、これらのフィールドを右側のチャート領域へドラッグ&ドロップします。真ん中にドラッグしてからでも調整できます。

チャートの種類を選択

右側上部のメニューから、棒グラフ(Bar)、折れ線グラフ(Line)など、好きなチャート形式を選択できます。また、積み上げ表示(Stacked)などの表示形式も指定可能です。

軸や集計の設定

「Horizontal axis(横軸)」や「Vertical axis(縦軸)」、さらに「Breakdown(内訳)」などの項目にフィールドをドラッグ&ドロップして、軸や表示方法を柔軟にカスタマイズできます。

素早い分析とフィルタリング

データのフィールドをクリックすると、各項目のトップ値や分布が表示され、即座にデータの傾向を把握できます。さらに、上部の検索バーで特定条件に合致するデータをフィルターすることも可能です。

軸の設定画面。実際に操作をし、各機能の理解を深めることを推奨します。

例)性別ごとの売上割合

割合の表示には用意されている機能だけではできないので、式の採用も必要。

Y軸:totalprice

Breakdown: sex

Y軸の「Appearance」→「Name」を「合計金額の割合」に設定

Y軸の「Formula」に `sum(totalprice)/overall_sum(sum(totalprice))` を入力

「男」と「女」のみを表示したい場合は、`sex_labes : (“男” or “女”)` を使用

Create a Runtime Field 

手順:

  1. Kibanaに移動し、Stack Managementを選択します。
  2. Data Viewsを開きます。
  3. 目的のData View(例:EC_site)を選択します。
  4. 右上にある “Add field”ボタンをクリックします。
  5. 以下のフィールドに入力します。

フィールド

名前

タイプ

フォーマット

スクリプト

sex_label

Keyword

(デフォルトのまま)

(下記👇参照)

表に年齢層を表示させたい時には以下の式でscriptを登録できます。

// for making the age group
def age = doc['age'].value;
if (age >= 15 && age <= 19) emit('15〜19歳');
else if (age >= 20 && age <= 24) emit('20〜24歳');
else if (age >= 25 && age <= 29) emit('25〜29歳');
else if (age >= 30 && age <= 34) emit('30〜34歳');
else if (age >= 35 && age <= 39) emit('35〜39歳');
else if (age >= 40 && age <= 44) emit('40〜44歳');
else if (age >= 45 && age <= 49) emit('45〜49歳');
else if (age >= 50 && age <= 54) emit('50〜54歳');
else if (age >= 55 && age <= 59) emit('55〜59歳');
else if (age >= 60) emit('60歳以上');
else emit('不明');

作成した様々な図やグラフを一つのダッシュボード機能に統合し、Kibanaの共有機能やレポート機能を活用することで、データ可視化と共有の効率化を図れます。今回のデータセットで作成した図※は、以下のダッシュボードにまとめました。ドラッグ&ドロップでサイズ調整ができるため、目的に応じて容易に活用可能です。

※ 図表の説明
売上 vs 性別(ラインチャート)
 各性別(女性/男性/その他)ごとの総売上額推移を示す。
年齢層 vs 件数(積み上げ棒グラフ)
 上位 10 の年齢グループ(例:30–34 歳、35–39 歳…)ごとの購入件数を集計。
 男女別に色分けし、どの世代でどちらの性別が多いかを可視化。
消費金額(ワードクラウド)
 都道府県別の総消費金額を文字の大きさで表現。
性別の割合(円グラフ)
 全体購入者に占める男女およびその他の比率をパーセンテージで表示。
商品 vs 金額(ラインチャート)
 各商品カテゴリ(PC デスク/ソファ/ベッド…など)ごとの平均購入金額を比較。
エリア vs 金額(テーブル)
 都道府県ごとに、男性・女性別の購入金額中央値を並べて表示。
注文履歴(時系列ラインチャート)
 7 日ごとに集計した総注文金額の推移を示す。
 時間軸を通して売上の増減トレンドを把握可能。

6. Elasticが持つAI × 統合分析力

Elasticは今や「全文検索ツール」ではなく、AI Search Platformとして以下の3領域を提供しています:

  • Search(ベクトル検索・セマンティック検索)
  • Observability(ログ・APM・メトリクス統合)
  • Security(SIEM・脅威検知)

今回のような構造化データだけでなく、商品説明・レビュー・チャット履歴など非構造データも、統合AIで文脈を理解した検索が可能です。OpenAIやAmazon Bedrockと連携し、ベクトル検索による高速類似検索も標準対応。

さらに、CSVアップロードに限らず、400以上のIntegrationでライブデータを収集し、即座にダッシュボード化できます。Elasticは自身でもElastic Cloud / Serverlessプラットフォームを提供しており、インフラ構築なしに拡張性のある環境をすぐに使い始めることが可能です。

7. まとめ & 次のステップ

  • CSVアップロード → インデックス整形 → 可視化まで、30分で体験可能
  • Kibana Lensで非エンジニアでも直感的に分析&共有
  • AI統合により、非構造データも“意味”で検索・分類が可能に
  • Integrationでライブデータをリアルタイム分析に活用

ElasticのAI Search Platformを活用することで、検索・分析・可視化・共有がワンストップで完結します。

💡 Elastic Cloudの無料トライアルもあります。今すぐ体験してみてください!