メインコンテンツまでスキップ

第 5 章: データから価値を掘り出す

集計とリスト操作で分析レベルを向上

Data Value Extraction

これまでの章では、データの検索と操作に焦点を当ててきました。この章では、Cypher の集計関数とリスト操作を駆使して、単なるデータ検索から一歩進んだ「データ分析」を行うスキルを身につけます。

5.1 基本集計:COUNT, SUM, AVG, MIN, MAX

ビジネスシナリオ

経営陣から「A 社」に関する主要な経営指標を求められました。具体的には、従業員総数、プロジェクトの総予算、そして従業員一人当たりの平均スキル保有数を算出する必要があります。

Basic Aggregation Functions

Cypher は、SQL でおなじみの標準的な集計関数をサポートしています。これらの関数は RETURN 句または WITH 句の中で使用され、マッチしたデータセット全体、あるいはグループ化されたデータセットに対して計算を行います。

基本的な集計クエリ

// A社の基本統計情報を一括取得
MATCH (e:Employee)-[:WORKS_FOR]->(c:Company {name: "A社"})
OPTIONAL MATCH (e)-[:PARTICIPATES_IN]->(p:Project)
RETURN c.name AS company,
count(DISTINCT e) AS totalEmployees,
avg(e.experience) AS avgExperience,
min(e.experience) AS minExperience,
max(e.experience) AS maxExperience,
count(DISTINCT p) AS activeProjects

プロジェクト予算分析

// 全プロジェクトの予算統計
MATCH (p:Project)
RETURN count(p) AS totalProjects,
sum(p.budget) AS totalBudget,
avg(p.budget) AS avgBudget,
min(p.budget) AS minBudget,
max(p.budget) AS maxBudget,
round(sum(p.budget) / 1000000.0, 2) AS totalBudgetInMillions

スキル習得状況の分析

// 従業員のスキル保有状況統計
MATCH (e:Employee)
OPTIONAL MATCH (e)-[:HAS_SKILL]->(s:Skill)
WITH e, count(s) AS skillCount
RETURN avg(skillCount) AS avgSkillsPerEmployee,
min(skillCount) AS minSkills,
max(skillCount) AS maxSkills,
count(CASE WHEN skillCount = 0 THEN 1 END) AS employeesWithNoSkills,
count(CASE WHEN skillCount >= 3 THEN 1 END) AS employeesWithManySkills

5.2 リストへの集約:collect()の活用

ビジネスシナリオ

各プロジェクトについて、参加している全従業員の名前とその役割をリスト形式で取得したい。また、プロジェクトマネージャーが各プロジェクトチームの構成を一目で把握できるようにしたい。

List Aggregation Concept

collect() は、グラフデータベースならではの非常に強力な集計関数です。これは、複数の行にまたがる値を一つのリストに集約します。個々の情報を失うことなくグループ化できるため、関連アイテムをまとめて扱う際に極めて有用です。

基本的なリスト集約

// プロジェクトごとのチームメンバー一覧
MATCH (p:Project)<-[r:PARTICIPATES_IN]-(e:Employee)
RETURN p.name AS projectName,
p.status AS status,
collect(e.name) AS teamMembers,
collect(r.role) AS memberRoles,
count(e) AS teamSize
ORDER BY teamSize DESC

構造化されたリスト作成

// より詳細なチーム構成情報
MATCH (p:Project)<-[r:PARTICIPATES_IN]-(e:Employee)
RETURN p.name AS projectName,
collect({
name: e.name,
title: e.title,
role: r.role,
experience: e.experience
}) AS teamComposition,
sum(e.experience) AS totalTeamExperience
ORDER BY totalTeamExperience DESC

階層的なデータ集約

// 会社ごとの部門別従業員構成
MATCH (c:Company)<-[:WORKS_FOR]-(e:Employee)
WITH c, e
MATCH (e)-[:HAS_SKILL]->(s:Skill)
RETURN c.name AS company,
c.industry AS industry,
collect(DISTINCT e.title) AS jobTitles,
collect(DISTINCT s.category) AS skillCategories,
collect({
employee: e.name,
title: e.title,
skills: collect(s.name)
}) AS employeeDetails

5.3 グルーピングとソート:ORDER BY, LIMIT

ビジネスシナリオ

人事部から「最も多くのスキルを持つ従業員トップ 5」と「各スキルカテゴリで最も熟練した従業員」のリストが求められました。

Grouping and Sorting

Cypher におけるグルーピングは「暗黙的」に行われます。RETURN 句や WITH 句で集計関数(例: count())と共に指定された非集計項目(例: e.name)が、自動的にグルーピングのキーとなります。

スキル保有数ランキング

// 最もスキル豊富な従業員トップ5
MATCH (e:Employee)-[:HAS_SKILL]->(s:Skill)
WITH e, count(s) AS skillCount, collect(s.name) AS skillList
RETURN e.name AS employee,
e.title AS title,
skillCount,
skillList
ORDER BY skillCount DESC, e.name
LIMIT 5

カテゴリ別エキスパート分析

// 各スキルカテゴリでの上級者ランキング
MATCH (e:Employee)-[r:HAS_SKILL]->(s:Skill)
WHERE r.level IN ['上級', 'エキスパート']
WITH s.category AS skillCategory, e, count(s) AS expertSkillCount
ORDER BY skillCategory, expertSkillCount DESC
WITH skillCategory, collect({
name: e.name,
title: e.title,
expertSkills: expertSkillCount
})[0..3] AS topExperts // 各カテゴリでトップ3のみ取得
RETURN skillCategory, topExperts

プロジェクト成功指標ランキング

// プロジェクトの投資対効果(ROI)分析
MATCH (p:Project)
WHERE p.status = "完了" AND p.actualBudget IS NOT NULL
WITH p,
round((p.budget - p.actualBudget) * 100.0 / p.budget, 2) AS budgetEfficiency,
duration.between(date(p.startDate), date(p.completedDate)).days AS projectDuration
RETURN p.name AS project,
p.budget AS plannedBudget,
p.actualBudget AS actualBudget,
budgetEfficiency AS budgetSavingsPercent,
projectDuration AS daysToComplete,
round(p.budget / projectDuration, 0) AS dailyBurnRate
ORDER BY budgetEfficiency DESC, projectDuration ASC
LIMIT 10

5.4 UNWIND:リストの展開とデータ変換

ビジネスシナリオ

人事部から、新たに追加すべきスキルのリスト("Go", "Rust", "GraphQL", "Kubernetes", "Docker")が提供されました。一つずつ CREATE 文を書くのではなく、効率的にデータを処理したい。

UNWIND Operation

UNWIND はcollect()の逆の操作を行う句です。リストを受け取り、その要素を一つずつ個別の行に展開します。これは、アプリケーションからパラメータとして渡されたリストを処理したり、JSON や CSV からデータを一括で取り込んだりする際に非常に強力なツールとなります。

基本的な UNWIND 操作

// 新しいスキルを一括で追加
WITH ["Go", "Rust", "GraphQL", "Kubernetes", "Docker"] AS newSkills
UNWIND newSkills AS skillName
MERGE (s:Skill {name: skillName})
ON CREATE SET s.category = "新技術",
s.demand = "高",
s.addedDate = date()
RETURN s.name AS addedSkill, s.category

複雑なデータ変換パイプライン

// プロジェクト参加履歴を分析して、協業パターンを抽出
MATCH (p:Project)<-[:PARTICIPATES_IN]-(e:Employee)
WITH p, collect(e) AS teamMembers
WHERE size(teamMembers) >= 2

// チームメンバーのペアを生成
UNWIND teamMembers AS member1
UNWIND teamMembers AS member2
WHERE id(member1) < id(member2) // 重複ペアを避ける

// 協業回数をカウント
WITH member1, member2, count(p) AS collaborationCount, collect(p.name) AS sharedProjects
WHERE collaborationCount >= 2 // 2回以上協業したペアのみ

RETURN member1.name AS employee1,
member2.name AS employee2,
collaborationCount,
sharedProjects
ORDER BY collaborationCount DESC

JSON データの処理

// 外部システムから取得したプロジェクト更新情報を処理
WITH [
{name: "AI導入支援", newStatus: "完了", completion: 95},
{name: "サプライチェーン最適化", newStatus: "進行中", completion: 60}
] AS projectUpdates

UNWIND projectUpdates AS update
MATCH (p:Project {name: update.name})
SET p.status = update.newStatus,
p.completion = update.completion,
p.lastUpdated = datetime()
RETURN p.name AS project, p.status, p.completion

5.5 高度な集計パターンとビジネス分析

組織のスキルギャップ分析

// 組織全体のスキル供給と需要のバランス分析
MATCH (requiredSkill:Skill)<-[:REQUIRES_SKILL]-(p:Project {status: "計画中"})
WITH requiredSkill, count(p) AS demand

MATCH (requiredSkill)<-[r:HAS_SKILL]-(e:Employee)
WHERE r.level IN ['上級', 'エキスパート']
WITH requiredSkill, demand, count(e) AS supply

RETURN requiredSkill.name AS skill,
requiredSkill.category AS category,
demand AS projectDemand,
supply AS qualifiedExperts,
CASE
WHEN supply >= demand THEN "充足"
WHEN supply * 2 >= demand THEN "要注意"
ELSE "不足"
END AS supplyStatus,
demand - supply AS gap
ORDER BY gap DESC, demand DESC

チーム生産性指標の算出

// プロジェクトチームの生産性指標を多角的に分析
MATCH (p:Project)<-[:PARTICIPATES_IN]-(e:Employee)
WHERE p.status = "完了"

WITH p,
count(e) AS teamSize,
sum(e.experience) AS totalExperience,
avg(e.experience) AS avgExperience,
collect(e.title) AS teamComposition

OPTIONAL MATCH (p)-[:HAS_SKILL_REQUIREMENT]->(requiredSkill:Skill)
WITH p, teamSize, totalExperience, avgExperience, teamComposition,
count(requiredSkill) AS complexityScore

RETURN p.name AS project,
teamSize,
round(avgExperience, 1) AS avgTeamExperience,
complexityScore,
round(p.budget / teamSize / 1000, 0) AS budgetPerMemberK,
round(totalExperience / teamSize * complexityScore, 0) AS productivityIndex,
teamComposition
ORDER BY productivityIndex DESC

Advanced Analytics Dashboard

時系列トレンド分析

// プロジェクト完了率の月次トレンド分析
MATCH (p:Project)
WHERE p.completedDate IS NOT NULL
WITH date.truncate('month', date(p.completedDate)) AS completionMonth,
count(p) AS completedProjects,
avg(duration.between(date(p.startDate), date(p.completedDate)).days) AS avgDuration

ORDER BY completionMonth
WITH collect({
month: completionMonth,
completed: completedProjects,
avgDuration: round(avgDuration, 1)
}) AS monthlyData

RETURN monthlyData

これらの高度な集計機能を活用することで、Cypher は単なるデータ取得ツールから、戦略的な意思決定を支援する強力なビジネスインテリジェンスプラットフォームへと進化します。次の章では、これまでの知識を総動員して、実際のビジネスシナリオでの課題解決に挑戦します。


前へ: 第 4 章: パターンを使いこなす | 次へ: 第 6 章: walk-with-ai 流・実践シナリオ

関連記事

著者: hnish