グラフデータベースを用いたカレー好きのソーシャルネットワーク分析：Neo4j と Python の統合

## はじめに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/c6af95da-279e-03a5-f42e-cedc600c6026.png)

本記事では、グラフデータベース Neo4j と Python を使用して、より複雑で現実的なカレー好きのソーシャルネットワークを分析する方法を解説します。この高度な分析を通じて、カレー愛好家たちの関係性、好みのパターン、人気のあるカレー店、そして個人の影響力などを詳細に可視化し、深い洞察を得ることを目指します。

## Neo4jとは？

![napkin-selection (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/55cedc6c-a5e3-574d-f142-2bbd92300bad.png)

Neo4jは、ノード（点）とリレーションシップ（線）でデータを表現するグラフデータベースの一種です。グラフデータベースは、複雑なデータ間の関係性を効率的に扱うことができ、従来のリレーショナルデータベースとは異なり、データ同士のリンクや関係性の解析に特化しています。Neo4jは、高速なクエリ処理やパフォーマンス、視覚的にデータを表現するための機能を持ち、ソーシャルネットワーク、推薦システム、詐欺検出など、関係性が重要となる多くのシナリオで使用されています。

Neo4jでは、Cypherという独自のクエリ言語を用いて、データの操作や検索を行います。CypherはSQLに似た文法で、グラフのノードやリレーションシップを簡単に操作することができ、特にネットワーク分析に適しています。

## 環境設定

### 前提条件
- Python 3.7以上
- Neo4j（ローカルインストールまたはDockerで実行）
- 必要なPythonライブラリ：neo4j、networkx、matplotlib

### セットアップ

1. Neo4jのインストール：
   - [Neo4j Desktop](https://neo4j.com/download/)をダウンロードしてインストール
   - または、Dockerを使用して Neo4j コンテナを実行

2. 必要なPythonライブラリのインストール：
   ```
   pip install neo4j networkx matplotlib
   ```

## データモデル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/b07601da-eb88-cd7c-9d96-a5f945326fc5.png)

今回のカレー好きネットワークは以下の要素で構成されています：

1. ノード：
   - Person: 名前、カレーの好み
   - Restaurant: 名前、専門のカレー、価格帯

2. 関係：
   - FAVORITE: 人物がお気に入りのレストラン（評価、訪問回数を含む）
   - VISITED: 人物が訪問したレストラン（評価、訪問回数を含む）
   - RECOMMENDS: 人物が別の人物を推薦する関係

## 主要な実装と出力例

### Neo4jへの接続

```python
from neo4j import GraphDatabase

class Neo4jConnection:
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def query(self, query, parameters=None):
        with self.driver.session() as session:
            try:
                result = session.run(query, parameters)
                return [record for record in result]
            except Exception as e:
                print(f"Query execution error: {e}")
                print(f"Problematic query: {query}")
                return None

def connect_to_neo4j():
    uri = input("Enter Neo4j URI (Default: bolt://localhost:7687): ") or "bolt://localhost:7687"
    user = input("Enter username (Default: neo4j): ") or "neo4j"
    password = input("Enter password (Default: password): ") or "password"
    return Neo4jConnection(uri, user, password)

# 接続例
conn = connect_to_neo4j()
print("Connected to Neo4j successfully!")
```

出力例：
```
Enter Neo4j URI (Default: bolt://localhost:7687): 
Using default value: bolt://localhost:7687
Enter username (Default: neo4j): 
Using default value: neo4j
Enter password (Default: password): 
Using default value: password
Connected to Neo4j successfully!
```

### ネットワークデータの作成

```python
def create_curry_network(conn):
    query = """
    // Create People
    CREATE (alice:Person {name: 'Alice', preference: 'Spicy'})
    CREATE (bob:Person {name: 'Bob', preference: 'Mild'})
    CREATE (charlie:Person {name: 'Charlie', preference: 'Veggie'})
    CREATE (david:Person {name: 'David', preference: 'Meaty'})
    CREATE (eve:Person {name: 'Eve', preference: 'Spicy'})
    CREATE (frank:Person {name: 'Frank', preference: 'Mild'})
    CREATE (grace:Person {name: 'Grace', preference: 'Veggie'})
    CREATE (henry:Person {name: 'Henry', preference: 'Meaty'})

    // Create Restaurants
    CREATE (spicyKing:Restaurant {name: 'Spicy King', specialty: 'Vindaloo Curry', price_range: 'Medium'})
    CREATE (greenCurry:Restaurant {name: 'Green Curry House', specialty: 'Vegetable Curry', price_range: 'Low'})
    CREATE (meatLover:Restaurant {name: 'Meat Lovers', specialty: 'Keema Curry', price_range: 'High'})
    CREATE (curryPalace:Restaurant {name: 'Curry Palace', specialty: 'Butter Chicken', price_range: 'Medium'})
    CREATE (spiceHeaven:Restaurant {name: 'Spice Heaven', specialty: 'Phaal Curry', price_range: 'High'})

    // Create FAVORITE relationships with ratings and visit counts
    CREATE (alice)-[:FAVORITE {rating: 5, visits: 10}]->(spicyKing)
    CREATE (bob)-[:FAVORITE {rating: 4, visits: 5}]->(greenCurry)
    CREATE (charlie)-[:FAVORITE {rating: 5, visits: 8}]->(greenCurry)
    CREATE (david)-[:FAVORITE {rating: 4, visits: 6}]->(meatLover)
    CREATE (eve)-[:FAVORITE {rating: 5, visits: 12}]->(spiceHeaven)
    CREATE (frank)-[:FAVORITE {rating: 4, visits: 7}]->(curryPalace)
    CREATE (grace)-[:FAVORITE {rating: 5, visits: 9}]->(greenCurry)
    CREATE (henry)-[:FAVORITE {rating: 4, visits: 6}]->(meatLover)

    // Create additional VISITED relationships
    CREATE (alice)-[:VISITED {rating: 3, visits: 2}]->(greenCurry)
    CREATE (bob)-[:VISITED {rating: 3, visits: 1}]->(meatLover)
    CREATE (charlie)-[:VISITED {rating: 4, visits: 3}]->(curryPalace)
    CREATE (david)-[:VISITED {rating: 5, visits: 4}]->(spicyKing)
    CREATE (eve)-[:VISITED {rating: 4, visits: 3}]->(meatLover)
    CREATE (frank)-[:VISITED {rating: 5, visits: 5}]->(spicyKing)
    CREATE (grace)-[:VISITED {rating: 3, visits: 2}]->(spiceHeaven)
    CREATE (henry)-[:VISITED {rating: 4, visits: 3}]->(curryPalace)

    // Create RECOMMENDS relationships
    CREATE (alice)-[:RECOMMENDS]->(bob)
    CREATE (bob)-[:RECOMMENDS]->(charlie)
    CREATE (charlie)-[:RECOMMENDS]->(david)
    CREATE (david)-[:RECOMMENDS]->(eve)
    CREATE (eve)-[:RECOMMENDS]->(frank)
    CREATE (frank)-[:RECOMMENDS]->(grace)
    CREATE (grace)-[:RECOMMENDS]->(henry)
    CREATE (henry)-[:RECOMMENDS]->(alice)
    """
    result = conn.query(query)
    if result is not None:
        print("Curry lovers network data created.")

# 実行例
create_curry_network(conn)
```

出力例：
```
Curry lovers network data created.
```

### お気に入りカレー店の分析

```python
def count_favorites(conn):
    query = """
    MATCH (r:Restaurant)<-[f:FAVORITE]-(p:Person)
    RETURN r.name AS restaurant, r.specialty AS specialty, r.price_range AS price_range,
           COUNT(p) AS favorite_count, AVG(f.rating) AS avg_rating, SUM(f.visits) AS total_visits
    ORDER BY favorite_count DESC
    """
    result = conn.query(query)
    if result:
        print("Favorite counts and ratings:")
        for record in result:
            print(f"{record['restaurant']} ({record['specialty']}, {record['price_range']}):")
            print(f"  Favorites: {record['favorite_count']}")
            print(f"  Average Rating: {record['avg_rating']:.2f}")
            print(f"  Total Visits: {record['total_visits']}")
    else:
        print("Failed to retrieve favorite counts.")

# 実行例
count_favorites(conn)
```

出力例：
```
Favorite counts and ratings:
Green Curry House (Vegetable Curry, Low):
  Favorites: 3
  Average Rating: 4.67
  Total Visits: 22
Meat Lovers (Keema Curry, High):
  Favorites: 2
  Average Rating: 4.00
  Total Visits: 12
Spicy King (Vindaloo Curry, Medium):
  Favorites: 1
  Average Rating: 5.00
  Total Visits: 10
Curry Palace (Butter Chicken, Medium):
  Favorites: 1
  Average Rating: 4.00
  Total Visits: 7
Spice Heaven (Phaal Curry, High):
  Favorites: 1
  Average Rating: 5.00
  Total Visits: 12
```

### カレー中心性の計算

```python
def calculate_curry_centrality(conn):
    query = """
    MATCH (p:Person)
    OPTIONAL MATCH (p)-[f:FAVORITE]->(r:Restaurant)
    OPTIONAL MATCH (p)-[v:VISITED]->(vr:Restaurant)
    OPTIONAL MATCH (p)-[:RECOMMENDS]->(friend:Person)
    RETURN p.name AS name, 
           p.preference AS preference,
           COUNT(DISTINCT r) AS favorite_restaurants,
           COUNT(DISTINCT vr) AS visited_restaurants,
           COUNT(DISTINCT friend) AS recommendations,
           AVG(f.rating) AS avg_favorite_rating,
           AVG(v.rating) AS avg_visit_rating,
           SUM(f.visits) + SUM(v.visits) AS total_visits
    ORDER BY total_visits DESC
    """
    result = conn.query(query)
    if result:
        print("\nCurry centrality scores:")
        for record in result:
            centrality = (record['favorite_restaurants'] + record['visited_restaurants'] + record['recommendations']) / 3
            print(f"{record['name']} (Preference: {record['preference']}):")
            print(f"  Centrality score = {centrality:.2f}")
            print(f"  Favorite Restaurants: {record['favorite_restaurants']}")
            print(f"  Visited Restaurants: {record['visited_restaurants']}")
            print(f"  Recommendations: {record['recommendations']}")
            print(f"  Avg Favorite Rating: {record['avg_favorite_rating']:.2f}")
            print(f"  Avg Visit Rating: {record['avg_visit_rating']:.2f}")
            print(f"  Total Visits: {record['total_visits']}")
    else:
        print("Failed to calculate centrality.")

# 実行例
calculate_curry_centrality(conn)
```

出力例：
```
Curry centrality scores:
Eve (Preference: Spicy):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 5.00
  Avg Visit Rating: 4.00
  Total Visits: 15
Alice (Preference: Spicy):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 5.00
  Avg Visit Rating: 3.00
  Total Visits: 12
Frank (Preference: Mild):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 4.00
  Avg Visit Rating: 5.00
  Total Visits: 12
Charlie (Preference: Veggie):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 5.00
  Avg Visit Rating: 4.00
  Total Visits: 11
Grace (Preference: Veggie):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 5.00
  Avg Visit Rating: 3.00
  Total Visits: 11
David (Preference: Meaty):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 4.00
  Avg Visit Rating: 5.00
  Total Visits: 10
Henry (Preference: Meaty):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 4.00
  Avg Visit Rating: 4.00
  Total Visits: 9
Bob (Preference: Mild):
  Centrality score = 1.00
  Favorite Restaurants: 1
  Visited Restaurants: 1
  Recommendations: 1
  Avg Favorite Rating: 4.00
  Avg Visit Rating: 3.00
  Total Visits: 6
```

### ネットワークの可視化

```python
import networkx as nx
import matplotlib.pyplot as plt

def visualize_curry_network(conn):
    query = """
    MATCH (n)
    OPTIONAL MATCH (n)-[r]->(m)
    RETURN n.name AS source, m.name AS target, TYPE(r) AS relation, LABELS(n)[0] AS source_type, LABELS(m)[0] AS target_type,
           CASE WHEN TYPE(r) IN ['FAVORITE', 'VISITED'] THEN r.rating ELSE NULL END AS rating,
           CASE WHEN TYPE(r) IN ['FAVORITE', 'VISITED'] THEN r.visits ELSE NULL END AS visits
    """
    result = conn.query(query)
    if result:
        G = nx.DiGraph()
        for record in result:
            source = record['source']
            target = record['target']
            relation = record['relation']
            source_type = record['source_type']
            target_type = record['target_type']
            rating = record['rating']
            visits = record['visits']
            
            G.add_node(source, node_type=source_type)
            if target:
                G.add_node(target, node_type=target_type)
                G.add_edge(source, target, relation=relation, rating=rating, visits=visits)
        
        plt.figure(figsize=(16, 12))
        # カスタムレイアウトの作成
        pos = nx.spring_layout(G, k=0.5, iterations=50)
        
        # ノードの描画
        person_nodes = [node for node, data in G.nodes(data=True) if data['node_type'] == 'Person']
        restaurant_nodes = [node for node, data in G.nodes(data=True) if data['node_type'] == 'Restaurant']
        
        nx.draw_networkx_nodes(G, pos, nodelist=person_nodes, node_color='lightblue', node_size=3000, alpha=0.8, label='Person')
        nx.draw_networkx_nodes(G, pos, nodelist=restaurant_nodes, node_color='lightgreen', node_size=3000, alpha=0.8, label='Restaurant')
        
        # エッジの描画
        favorite_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'FAVORITE']
        visited_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'VISITED']
        recommend_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'RECOMMENDS']
        
        nx.draw_networkx_edges(G, pos, edgelist=favorite_edges, edge_color='r', arrows=True, label='Favorite')
        nx.draw_networkx_edges(G, pos, edgelist=visited_edges, edge_color='g', arrows=True, label='Visited')
        nx.draw_networkx_edges(G, pos, edgelist=recommend_edges, edge_color='b', style='dashed', arrows=True, label='Recommends')
        
        # ラベルの描画
        nx.draw_networkx_labels(G, pos, font_size=8, font_family='sans-serif')
        
        # エッジラベルの描画 (評価とビジット回数)
        edge_labels = {(u, v): f"R:{d['rating']}, V:{d['visits']}" for (u, v, d) in G.edges(data=True) if 'rating' in d and 'visits' in d}
        nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=6)
        
        # 凡例
        plt.legend(loc='upper left', bbox_to_anchor=(0.02, 0.98))
        
        plt.title("Curry Lovers Network")
        plt.axis('off')
        plt.tight_layout()
        plt.savefig("curry_network.png", dpi=300, bbox_inches='tight')
        print("Network graph saved as 'curry_network.png'.")
    else:
        print("Failed to retrieve network data. Visualization cannot be performed.")

# 実行例
visualize_curry_network(conn)
```

出力例：
```
Network graph saved as 'curry_network.png'.
```

![curry_network.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/8509407b-20ad-0b99-7173-82f35efffd5e.png)

Neo4j Browserでの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0b4896a5-e17a-905f-d39c-8f8d100f977c.png)


## 分析結果の解釈

![napkin-selection (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d9ee7ff0-92ce-a8f5-fe42-4630e0c5c0d4.png)

1. **お気に入りカレー店の分析**：
   - Green Curry House が最も人気があり、3人のお気に入りを獲得しています。また、平均評価も高く（4.67）、総訪問回数も最多（22回）です。
   - Spicy King と Spice Heaven は平均評価が最も高い（5.00）ですが、お気に入りとして選んだ人は各1人のみです。
   - 価格帯による明確な傾向は見られませんが、低価格のGreen Curry Houseが最も人気があることは注目に値します。

2. **カレー中心性スコア**：
   - すべての人物の中心性スコアが1.00となっており、ネットワーク内での活動レベルが均等であることを示しています。
   - しかし、総訪問回数に注目すると、Eve（15回）とAlice（12回）が最も活発に店を訪れていることがわかります。
   - 辛いカレーを好む人（Eve, Alice）が最も活発に店を訪れる傾向があります。

3. **ネットワーク可視化**：
   - 人物とレストランの関係性が視覚的に表現され、誰がどのレストランを好み、訪れているかが一目で分かります。
   - 推薦関係（RECOMMENDS）が円環状になっており、情報やトレンドが循環する可能性を示唆しています。

## 高度な分析と応用

1. **嗜好パターンの特定**：
   人物の好みとお気に入りのレストランの専門カレーを比較し、一致度を分析します。
   ```cypher
   MATCH (p:Person)-[f:FAVORITE]->(r:Restaurant)
   RETURN p.name, p.preference, r.name, r.specialty, f.rating
   ORDER BY f.rating DESC
   ```

2. **価格帯と人気度の相関**：
   レストランの価格帯と人気度（お気に入り数や平均評価）の関係を分析します。
   ```cypher
   MATCH (r:Restaurant)<-[f:FAVORITE]-()
   RETURN r.name, r.price_range, COUNT(f) as favorite_count, AVG(f.rating) as avg_rating
   ORDER BY favorite_count DESC
   ```

3. **影響力のある人物の特定**：
   推薦数と、推薦した人物のお気に入りレストランの重複度を分析し、影響力を測定します。
   ```cypher
   MATCH (p1:Person)-[:RECOMMENDS]->(p2:Person)
   MATCH (p1)-[:FAVORITE]->(r:Restaurant)<-[:FAVORITE]-(p2)
   RETURN p1.name, COUNT(DISTINCT p2) as recommendations, COUNT(DISTINCT r) as shared_favorites
   ORDER BY recommendations DESC, shared_favorites DESC
   ```

4. **コミュニティ検出**：
   類似した好みや行動パターンを持つ人物グループを特定します。これには、NetworkXのコミュニティ検出アルゴリズムを使用できます。

5. **時系列分析**：
   訪問データに時間情報を追加し、人気トレンドの変化や個人の好みの変遷を追跡します。

## 結論

この高度なカレー好きのソーシャルネットワーク分析を通じて、以下のような深い洞察を得ることができました：

1. 最も人気のあるカレー店は「Green Curry House」で、特に高い平均評価を得ています。
2. 辛いカレーを好む人（Eve, Alice）が最も活発に店を訪れる傾向があります。
3. すべての参加者が同程度のネットワーク中心性を持っていますが、訪問回数には個人差があります。
4. 価格帯による明確な人気の差は見られませんが、低価格帯の店（Green Curry House）が最も人気があります。

![napkin-selection (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/105be273-3eb8-e15c-4c17-1bd95423011f.png)


これらの洞察は、以下のような実践的な応用が可能です：

- レストラン経営者：メニュー開発、価格設定、マーケティング戦略の立案に活用できます。
- マーケッター：インフルエンサーマーケティングの戦略立案や、ターゲット顧客の特定に役立ちます。
- 食品業界の研究者：消費者行動や食の嗜好の傾向分析に応用できます。

グラフデータベースとPythonを組み合わせることで、複雑なネットワーク構造を持つデータを効率的に分析し、有益な洞察を得ることができます。この手法は、カレー好きのネットワーク以外にも、様々な分野でのソーシャルネットワーク分析や関係性分析に応用可能です。

今後の発展として、より大規模なデータセットの分析、時系列データの導入、機械学習アルゴリズムとの統合などが考えられます。これらの発展により、さらに深い洞察や予測モデルの構築が可能になるでしょう。

![napkin-selection (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/7688ec49-a010-633a-32dd-5ed9fd7ca9b1.png)

## 実装(動作確認用)

```python
import sys
import io
from neo4j import GraphDatabase
import networkx as nx
import matplotlib.pyplot as plt
import random

# エンコーディング設定
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

class Neo4jConnection:
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def query(self, query, parameters=None):
        with self.driver.session() as session:
            try:
                result = session.run(query, parameters)
                return [record for record in result]
            except Exception as e:
                print(f"Query execution error: {e}")
                print(f"Problematic query: {query}")
                return None

def get_input_with_default(prompt, default):
    user_input = input(f"{prompt} (Default: {default}): ").strip()
    if not user_input:
        print(f"Using default value: {default}")
        return default
    return user_input

def connect_to_neo4j():
    uri = get_input_with_default("Enter Neo4j URI", "bolt://localhost:7687")
    user = get_input_with_default("Enter username", "neo4j")
    password = get_input_with_default("Enter password", "password")
    return Neo4jConnection(uri, user, password)

def create_curry_network(conn):
    query = """
    // Create People
    CREATE (alice:Person {name: 'Alice', preference: 'Spicy'})
    CREATE (bob:Person {name: 'Bob', preference: 'Mild'})
    CREATE (charlie:Person {name: 'Charlie', preference: 'Veggie'})
    CREATE (david:Person {name: 'David', preference: 'Meaty'})
    CREATE (eve:Person {name: 'Eve', preference: 'Spicy'})
    CREATE (frank:Person {name: 'Frank', preference: 'Mild'})
    CREATE (grace:Person {name: 'Grace', preference: 'Veggie'})
    CREATE (henry:Person {name: 'Henry', preference: 'Meaty'})

    // Create Restaurants
    CREATE (spicyKing:Restaurant {name: 'Spicy King', specialty: 'Vindaloo Curry', price_range: 'Medium'})
    CREATE (greenCurry:Restaurant {name: 'Green Curry House', specialty: 'Vegetable Curry', price_range: 'Low'})
    CREATE (meatLover:Restaurant {name: 'Meat Lovers', specialty: 'Keema Curry', price_range: 'High'})
    CREATE (curryPalace:Restaurant {name: 'Curry Palace', specialty: 'Butter Chicken', price_range: 'Medium'})
    CREATE (spiceHeaven:Restaurant {name: 'Spice Heaven', specialty: 'Phaal Curry', price_range: 'High'})

    // Create FAVORITE relationships with ratings and visit counts
    CREATE (alice)-[:FAVORITE {rating: 5, visits: 10}]->(spicyKing)
    CREATE (bob)-[:FAVORITE {rating: 4, visits: 5}]->(greenCurry)
    CREATE (charlie)-[:FAVORITE {rating: 5, visits: 8}]->(greenCurry)
    CREATE (david)-[:FAVORITE {rating: 4, visits: 6}]->(meatLover)
    CREATE (eve)-[:FAVORITE {rating: 5, visits: 12}]->(spiceHeaven)
    CREATE (frank)-[:FAVORITE {rating: 4, visits: 7}]->(curryPalace)
    CREATE (grace)-[:FAVORITE {rating: 5, visits: 9}]->(greenCurry)
    CREATE (henry)-[:FAVORITE {rating: 4, visits: 6}]->(meatLover)

    // Create additional VISITED relationships
    CREATE (alice)-[:VISITED {rating: 3, visits: 2}]->(greenCurry)
    CREATE (bob)-[:VISITED {rating: 3, visits: 1}]->(meatLover)
    CREATE (charlie)-[:VISITED {rating: 4, visits: 3}]->(curryPalace)
    CREATE (david)-[:VISITED {rating: 5, visits: 4}]->(spicyKing)
    CREATE (eve)-[:VISITED {rating: 4, visits: 3}]->(meatLover)
    CREATE (frank)-[:VISITED {rating: 5, visits: 5}]->(spicyKing)
    CREATE (grace)-[:VISITED {rating: 3, visits: 2}]->(spiceHeaven)
    CREATE (henry)-[:VISITED {rating: 4, visits: 3}]->(curryPalace)

    // Create RECOMMENDS relationships
    CREATE (alice)-[:RECOMMENDS]->(bob)
    CREATE (bob)-[:RECOMMENDS]->(charlie)
    CREATE (charlie)-[:RECOMMENDS]->(david)
    CREATE (david)-[:RECOMMENDS]->(eve)
    CREATE (eve)-[:RECOMMENDS]->(frank)
    CREATE (frank)-[:RECOMMENDS]->(grace)
    CREATE (grace)-[:RECOMMENDS]->(henry)
    CREATE (henry)-[:RECOMMENDS]->(alice)
    """
    result = conn.query(query)
    if result is not None:
        print("Curry lovers network data created.")

def count_favorites(conn):
    query = """
    MATCH (r:Restaurant)<-[f:FAVORITE]-(p:Person)
    RETURN r.name AS restaurant, r.specialty AS specialty, r.price_range AS price_range,
           COUNT(p) AS favorite_count, AVG(f.rating) AS avg_rating, SUM(f.visits) AS total_visits
    ORDER BY favorite_count DESC
    """
    result = conn.query(query)
    if result:
        print("Favorite counts and ratings:")
        for record in result:
            print(f"{record['restaurant']} ({record['specialty']}, {record['price_range']}):")
            print(f"  Favorites: {record['favorite_count']}")
            print(f"  Average Rating: {record['avg_rating']:.2f}")
            print(f"  Total Visits: {record['total_visits']}")
    else:
        print("Failed to retrieve favorite counts.")

def calculate_curry_centrality(conn):
    query = """
    MATCH (p:Person)
    OPTIONAL MATCH (p)-[f:FAVORITE]->(r:Restaurant)
    OPTIONAL MATCH (p)-[v:VISITED]->(vr:Restaurant)
    OPTIONAL MATCH (p)-[:RECOMMENDS]->(friend:Person)
    RETURN p.name AS name, 
           p.preference AS preference,
           COUNT(DISTINCT r) AS favorite_restaurants,
           COUNT(DISTINCT vr) AS visited_restaurants,
           COUNT(DISTINCT friend) AS recommendations,
           AVG(f.rating) AS avg_favorite_rating,
           AVG(v.rating) AS avg_visit_rating,
           SUM(f.visits) + SUM(v.visits) AS total_visits
    ORDER BY total_visits DESC
    """
    result = conn.query(query)
    if result:
        print("\nCurry centrality scores:")
        for record in result:
            centrality = (record['favorite_restaurants'] + record['visited_restaurants'] + record['recommendations']) / 3
            print(f"{record['name']} (Preference: {record['preference']}):")
            print(f"  Centrality score = {centrality:.2f}")
            print(f"  Favorite Restaurants: {record['favorite_restaurants']}")
            print(f"  Visited Restaurants: {record['visited_restaurants']}")
            print(f"  Recommendations: {record['recommendations']}")
            print(f"  Avg Favorite Rating: {record['avg_favorite_rating']:.2f}")
            print(f"  Avg Visit Rating: {record['avg_visit_rating']:.2f}")
            print(f"  Total Visits: {record['total_visits']}")
    else:
        print("Failed to calculate centrality.")

def visualize_curry_network(conn):
    query = """
    MATCH (n)
    OPTIONAL MATCH (n)-[r]->(m)
    RETURN n.name AS source, m.name AS target, TYPE(r) AS relation, LABELS(n)[0] AS source_type, LABELS(m)[0] AS target_type,
           CASE WHEN TYPE(r) IN ['FAVORITE', 'VISITED'] THEN r.rating ELSE NULL END AS rating,
           CASE WHEN TYPE(r) IN ['FAVORITE', 'VISITED'] THEN r.visits ELSE NULL END AS visits
    """
    result = conn.query(query)
    if result:
        G = nx.DiGraph()
        for record in result:
            source = record['source']
            target = record['target']
            relation = record['relation']
            source_type = record['source_type']
            target_type = record['target_type']
            rating = record['rating']
            visits = record['visits']
            
            G.add_node(source, node_type=source_type)
            if target:
                G.add_node(target, node_type=target_type)
                G.add_edge(source, target, relation=relation, rating=rating, visits=visits)
        
        plt.figure(figsize=(16, 12))
        
        # カスタムレイアウトの作成
        pos = nx.spring_layout(G, k=0.5, iterations=50)
        
        # ノードの描画
        person_nodes = [node for node, data in G.nodes(data=True) if data['node_type'] == 'Person']
        restaurant_nodes = [node for node, data in G.nodes(data=True) if data['node_type'] == 'Restaurant']
        
        nx.draw_networkx_nodes(G, pos, nodelist=person_nodes, node_color='lightblue', node_size=3000, alpha=0.8, label='Person')
        nx.draw_networkx_nodes(G, pos, nodelist=restaurant_nodes, node_color='lightgreen', node_size=3000, alpha=0.8, label='Restaurant')
        
        # エッジの描画
        favorite_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'FAVORITE']
        visited_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'VISITED']
        recommend_edges = [(u, v) for (u, v, d) in G.edges(data=True) if d['relation'] == 'RECOMMENDS']
        
        nx.draw_networkx_edges(G, pos, edgelist=favorite_edges, edge_color='r', arrows=True, label='Favorite')
        nx.draw_networkx_edges(G, pos, edgelist=visited_edges, edge_color='g', arrows=True, label='Visited')
        nx.draw_networkx_edges(G, pos, edgelist=recommend_edges, edge_color='b', style='dashed', arrows=True, label='Recommends')
        
        # ラベルの描画
        nx.draw_networkx_labels(G, pos, font_size=8, font_family='sans-serif')
        
        # エッジラベルの描画 (評価とビジット回数)
        edge_labels = {(u, v): f"R:{d['rating']}, V:{d['visits']}" for (u, v, d) in G.edges(data=True) if 'rating' in d and 'visits' in d}
        nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=6)
        
        # 凡例
        plt.legend(loc='upper left', bbox_to_anchor=(0.02, 0.98))
        
        plt.title("Curry Lovers Network")
        plt.axis('off')
        plt.tight_layout()
        plt.savefig("curry_network.png", dpi=300, bbox_inches='tight')
        print("Network graph saved as 'curry_network.png'.")
    else:
        print("Failed to retrieve network data. Visualization cannot be performed.")

def drop_database(conn):
    query = "MATCH (n) DETACH DELETE n"
    result = conn.query(query)
    if result is not None:
        print("All nodes and relationships have been deleted from the database.")
    else:
        print("Failed to delete the database contents.")

def main():
    print("Welcome to the Curry Lovers Network Analysis Script!")
    conn = connect_to_neo4j()

    while True:
        print("\nSelect an operation:")
        print("1: Create curry lovers network data")
        print("2: Count favorite curry restaurants")
        print("3: Calculate curry centrality")
        print("4: Visualize network")
        print("5: Delete all data")
        print("6: Exit")

        choice = get_input_with_default("Choose (1-6)", "6")

        if choice == '1':
            create_curry_network(conn)
        elif choice == '2':
            count_favorites(conn)
        elif choice == '3':
            calculate_curry_centrality(conn)
        elif choice == '4':
            visualize_curry_network(conn)
        elif choice == '5':
            confirm = input("Are you sure you want to delete all data? (yes/no): ").lower()
            if confirm == 'yes':
                drop_database(conn)
            else:
                print("Data deletion cancelled.")
        elif choice == '6':
            print("Exiting the program.")
            conn.close()
            sys.exit(0)
        else:
            print("Invalid choice. Please enter a number between 1 and 6.")

        input("\nPress Enter to continue...")

if __name__ == "__main__":
    main()
```

```mermaid
sequenceDiagram
    actor ユーザー
    participant Python as Pythonスクリプト
    participant Neo4j as Neo4jデータベース

    ユーザー->>Python: スクリプト実行
    Python->>Neo4j: 接続情報入力
    Neo4j-->>Python: 接続確立
    Python->>ユーザー: 接続成功メッセージ

    ユーザー->>Python: ネットワークデータ作成指示
    Python->>Neo4j: create_curry_network()実行
    Neo4j-->>Python: データ作成完了
    Python->>ユーザー: データ作成完了メッセージ

    ユーザー->>Python: お気に入りカレー店分析指示
    Python->>Neo4j: count_favorites()実行
    Neo4j-->>Python: お気に入り数、評価、訪問回数データ
    Python->>ユーザー: 分析結果表示

    ユーザー->>Python: カレー中心性計算指示
    Python->>Neo4j: calculate_curry_centrality()実行
    Neo4j-->>Python: 中心性スコア、評価、訪問データ
    Python->>ユーザー: 中心性計算結果表示

    ユーザー->>Python: ネットワーク可視化指示
    Python->>Neo4j: visualize_curry_network()のデータ取得
    Neo4j-->>Python: ノードとエッジデータ
    Python->>Python: NetworkXでグラフ構築
    Python->>Python: Matplotlibで描画
    Python->>ユーザー: ネットワーク図表示・保存

    ユーザー->>Python: 分析終了指示
    Python->>Neo4j: 接続終了
    Python->>ユーザー: プログラム終了メッセージ
```