图数据库:个性化图书推荐。


Graph Databases: Personalized Book Recommendations

一、图数据库建模优化(Cypher部分)​​

1. 扩展数据模型 - 添加更多关系和属性

// 1.1 添加图书主题和关键词节点
CREATE (t1:Topic {name:"数据库"}), 
       (t2:Topic {name:"文学"}),
       (k1:Keyword {name:"图算法"}), 
       (k2:Keyword {name:"魔幻现实主义"})
// 1.2 建立图书-主题关系
MATCH (b:Book), (t:Topic)
WHERE b.book_id IN ["B001","B002"] AND t.name = "数据库"
CREATE (b)-[:HAS_TOPIC]->(t)
// 1.3 添加读者行为数据
MATCH (r:Reader {reader_id:"R001"})
SET r += {
    reading_speed: 2.5,    // 阅读速度(本/月)
    preferred_time: "night" // 偏好晚间阅读
}
// 1.4 建立图书相似关系(基于共同借阅)
MATCH (b1:Book)<-[:BORROWED]-(:Reader)-[:BORROWED]->(b2:Book)
WHERE b1 <> b2
MERGE (b1)-[s:SIMILAR {weight: count(*)}]-(b2)

2. 用户画像查询

// 2.1 获取读者兴趣图谱
MATCH (r:Reader {reader_id:"R001"})-[:BORROWED]->(b:Book)-[:HAS_TOPIC]->(t:Topic)
RETURN r.name AS reader, 
       collect(DISTINCT t.name) AS interests,
       avg(b.rating) AS avg_rating
// 2.2 发现读者社区(基于相似借阅模式)
MATCH (r1:Reader {reader_id:"R001"})-[:BORROWED]->()<-[:BORROWED]-(r2:Reader)
WITH r1, r2, count(*) AS overlap
WHERE overlap > 2
RETURN r2.name AS similar_reader, 
       overlap
ORDER BY overlap DESC

二、Python图分析与推荐实现​​

1. 图嵌入与特征提取

import networkx as nx
from node2vec import Node2Vec
from sklearn.cluster import KMeans

# 从Neo4j导入数据构建NetworkX图
def load_graph_from_neo4j():
    G = nx.Graph()
    
    # 添加节点和边(示例代码,实际需连接Neo4j)
    G.add_node("R001", type='Reader', name='王小明')
    G.add_node("B001", type='Book', title='Neo4j图数据库实战')
    G.add_edge("R001", "B001", relation='BORROWED', weight=3)
    
    return G

# 使用Node2Vec生成图嵌入
def generate_embeddings(G):
    # 参数设置:p=1.0, q=0.5 控制随机游走策略
    node2vec = Node2Vec(G, dimensions=64, walk_length=30, 
                       num_walks=200, p=1, q=0.5, workers=4)
    model = node2vec.fit(window=10, min_count=1)
    return model

# 聚类分析读者兴趣
def cluster_readers(model, reader_ids):
    vectors = [model.wv[rid] for rid in reader_ids]
    kmeans = KMeans(n_clusters=5).fit(vectors)
    return kmeans.labels_

2. 个性化推荐算法

import numpy as np
from scipy.spatial.distance import cosine

# 基于图嵌入的推荐
def graph_based_recommend(model, reader_id, top_n=5):
    # 获取读者和图书的嵌入向量
    reader_vec = model.wv[reader_id]
    book_vecs = {n: model.wv[n] for n in model.wv.key_to_index 
                if n.startswith('B')}
    
    # 计算余弦相似度
    similarities = {
        book: 1 - cosine(reader_vec, vec)
        for book, vec in book_vecs.items()
    }
    
    # 返回TopN推荐
    return sorted(similarities.items(), 
                 key=lambda x: x[1], reverse=True)[:top_n]

# 混合推荐策略(结合协同过滤)
def hybrid_recommendation(reader_id):
    # 从Neo4j获取协同过滤结果
    cypher_query = """
    MATCH (target:Reader {reader_id: $reader_id})-[:BORROWED]->()<-[:BORROWED]-(other:Reader)
    WITH other, count(*) AS strength
    ORDER BY strength DESC LIMIT 10
    MATCH (other)-[:BORROWED]->(rec:Book)
    WHERE NOT (target)-[:BORROWED]->(rec)
    RETURN rec.book_id AS book, sum(strength) AS score
    ORDER BY score DESC LIMIT 5
    """
    cf_results = neo4j_session.run(cypher_query, reader_id=reader_id)
    
    # 获取图嵌入推荐结果
    graph_recs = graph_based_recommend(model, reader_id)
    
    # 混合排序算法
    combined = {}
    for book, score in cf_results:
        combined[book] = combined.get(book, 0) + score*0.7
        
    for book, sim in graph_recs:
        combined[book] = combined.get(book, 0) + sim*0.3
    
    return sorted(combined.items(), key=lambda x: x[1], reverse=True)[:5]

三、完整工作流程实现​:关键实现步骤

1. 数据增强(每日运行)

# 更新图书相似度关系
def update_book_similarity():
    cypher = """
    MATCH (b1:Book)<-[:BORROWED]-(r:Reader)-[:BORROWED]->(b2:Book)
    WHERE b1 <> b2
    MERGE (b1)-[s:SIMILAR]->(b2)
    SET s.weight = COALESCE(s.weight,0) + 1
    """
    neo4j_session.run(cypher)

2.实时推荐API

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/recommend/<reader_id>')
def get_recommendations(reader_id):
    recommendations = hybrid_recommendation(reader_id)
    return jsonify({
        "reader_id": reader_id,
        "recommendations": [
            {"book_id": b, "score": float(s)} 
            for b,s in recommendations
        ]
    })

3.读者画像可视化

def visualize_reader_profile(reader_id):
    # 从Neo4j获取多维特征
    query = """
    MATCH (r:Reader {reader_id: $id})-[:BORROWED]->(b:Book)
    OPTIONAL MATCH (b)-[:HAS_TOPIC]->(t:Topic)
    RETURN {
        basic_info: properties(r),
        reading_history: collect(DISTINCT b.title),
        interests: collect(DISTINCT t.name),
        reading_metrics: {
            total_books: count(DISTINCT b),
            avg_borrow_duration: avg(duration.inDays(
                date(br.borrow_date), 
                date(coalesce(br.return_date, date())))
            )
        }
    } AS profile
    """
    profile = neo4j_session.run(query, id=reader_id).single()["profile"]
    
    # 使用Matplotlib生成雷达图
    metrics = profile['reading_metrics']
    labels = ['阅读量', '阅读深度', '阅读速度', '主题广度']
    values = [
        metrics['total_books'],
        metrics['avg_borrow_duration'],
        profile['basic_info']['reading_speed'],
        len(set(profile['interests']))
    ]
    
    # 绘制雷达图(代码略)
    return radar_chart

四、应用场景实现​​

1. 动态资源优化

// 发现热门主题关联
MATCH (t1:Topic)<-[:HAS_TOPIC]-(:Book)<-[:BORROWED]-(:Reader)-[:BORROWED]->(:Book)-[:HAS_TOPIC]->(t2:Topic)
WHERE t1 <> t2
RETURN t1.name AS topic1, 
       t2.name AS topic2, 
       count(*) AS strength
ORDER BY strength DESC
LIMIT 10

2.个性化推送触发

# 检查新书是否符合读者兴趣
def check_new_book_match(book_id, reader_id):
    reader_vec = model.wv[reader_id]
    book_vec = model.wv[book_id]
    similarity = 1 - cosine(reader_vec, book_vec)
    return similarity > 0.7  # 阈值可调整

3.读者流失预警

# 基于图特征预测流失风险
def predict_churn_risk(reader_id):
    query = """
    MATCH (r:Reader {reader_id: $id})
    OPTIONAL MATCH (r)-[br:BORROWED]->()
    RETURN {
        last_activity: duration.between(
            date(br.borrow_date), 
            date()).days,
        community_engagement: size((
            MATCH (r)-[:BORROWED]->()<-[:BORROWED]-(:Reader)
            RETURN 1
        ))
    } AS features
    """
    features = neo4j_session.run(query, id=reader_id).single()["features"]
    # 使用预训练模型预测(示例逻辑)
    risk_score = 0.3*features['last_activity'] - 0.7*features['community_engagement']
    return max(0, min(1, risk_score))

五、性能优化

1 图数据库优化

1.1 为频繁查询路径创建索引

CREATE INDEX FOR (r:Reader) ON (r.reader_id)
CREATE INDEX FOR (b:Book) ON (b.book_id)

1.2 使用APOC库的图算法

CALL apoc.algo.pageRank(['Book','Reader'], 'BORROWED')

2 ​​Python层优化​​

使用Dask并行处理大规模图计算;
缓存图嵌入模型(推荐使用Joblib);
增量更新策略减少全图计算。

3 推荐多样性保障​

def diversify_recommendations(recommendations, top_n=5):
    # 按主题聚类推荐结果
    topics = get_book_topics([b for b,_ in recommendations])
    clustered = {}
    for (book, score), topic in zip(recommendations, topics):
        clustered.setdefault(topic, []).append((book, score))

    # 从每个主题选取代表
    final = []
    for topic in clustered:
        final.append(max(clustered[topic], key=lambda x: x[1]))
    return sorted(final, key=lambda x: x[1], reverse=True)[:top_n]

六、方案小结

深度图特征挖掘​​:利用图嵌入捕获高阶关系。
​​多策略融合​​:结合协同过滤与图算法。
​​实时动态更新​​:持续优化用户画像。
​​可解释性设计​​:通过可视化解释推荐逻辑。

七、附另一份完整代码

import matplotlib.pyplot as plt
import numpy as np
from neo4j import GraphDatabase
from datetime import datetime
import logging

class ReaderProfileVisualizer:
    def __init__(self, neo4j_uri, username, password):
        """
        初始化Neo4j数据库连接
        """
        self.driver = GraphDatabase.driver(neo4j_uri, auth=(username, password))
        self.setup_plot_style()
    
    def setup_plot_style(self):
        """设置图表样式"""
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial']  # 支持中文显示
        plt.rcParams['axes.unicode_minus'] = False
    
    def get_reader_profile(self, reader_id):
        """
        从Neo4j获取读者画像数据(优化后的查询)
        """
        # 优化的Cypher查询
        query = """
        MATCH (r:Reader {reader_id: $reader_id})-[br:BORROWED]->(b:Book)
        OPTIONAL MATCH (b)-[:HAS_TOPIC]->(t:Topic)
        WITH r, br, b, t
        WHERE br.borrow_date IS NOT NULL  // 确保借阅日期存在
        RETURN {
            basic_info: properties(r),
            reading_history: collect(DISTINCT b.title),
            interests: collect(DISTINCT t.name),
            reading_metrics: {
                total_books: count(DISTINCT b),
                avg_borrow_duration: avg(
                    duration.inDays(
                        date(br.borrow_date), 
                        date(coalesce(br.return_date, date()))
                    ).days
                ),
                unique_topics: count(DISTINCT t.name),
                completion_rate: toFloat(
                    count(CASE WHEN br.return_date IS NOT NULL THEN 1 ELSE NULL END)
                ) / count(br) * 100
            }
        } AS profile
        """
        
        try:
            with self.driver.session() as session:
                result = session.run(query, reader_id=reader_id)
                record = result.single()
                
                if record is None:
                    raise ValueError(f"未找到读者ID: {reader_id}")
                
                profile = record["profile"]
                return self._validate_profile(profile)
                
        except Exception as e:
            logging.error(f"数据库查询错误: {e}")
            raise
    
    def _validate_profile(self, profile):
        """
        验证和清理画像数据
        """
        # 处理可能的空值
        metrics = profile.get('reading_metrics', {})
        basic_info = profile.get('basic_info', {})
        
        # 设置默认值
        metrics.setdefault('total_books', 0)
        metrics.setdefault('avg_borrow_duration', 0)
        metrics.setdefault('unique_topics', 0)
        metrics.setdefault('completion_rate', 0)
        
        basic_info.setdefault('reading_speed', 0)
        
        # 确保interests是列表
        profile['interests'] = profile.get('interests', []) or []
        
        return profile
    
    def normalize_metrics(self, values, max_values):
        """
        标准化指标值到0-1范围,用于雷达图显示
        """
        normalized = []
        for i, value in enumerate(values):
            if max_values[i] > 0:
                normalized.append(min(value / max_values[i], 1.0))
            else:
                normalized.append(0)
        return normalized
    
    def create_radar_chart(self, profile, save_path=None):
        """
        创建雷达图可视化
        """
        metrics = profile['reading_metrics']
        basic_info = profile['basic_info']
        
        # 定义雷达图指标
        categories = ['阅读量', '阅读深度', '阅读速度', '主题广度', '完成率']
        
        # 原始值
        raw_values = [
            metrics['total_books'],                    # 阅读量
            metrics['avg_borrow_duration'],           # 平均借阅时长(阅读深度)
            basic_info.get('reading_speed', 0),       # 阅读速度
            metrics['unique_topics'],                  # 主题广度
            metrics['completion_rate'] / 100          # 完成率(转换为0-1)
        ]
        
        # 设置各指标的最大值(可根据实际情况调整)
        max_values = [50, 30, 100, 20, 1.0]  # 示例最大值
        
        # 标准化值
        values = self.normalize_metrics(raw_values, max_values)
        
        # 创建雷达图
        fig = plt.figure(figsize=(10, 8))
        ax = fig.add_subplot(111, polar=True)
        
        # 计算角度
        angles = np.linspace(0, 2 * np.pi, len(categories), endpoint=False).tolist()
        angles += angles[:1]  # 闭合图形
        values += values[:1]
        
        # 绘制雷达图
        ax.plot(angles, values, 'o-', linewidth=2, label='读者画像', color='#2E86AB')
        ax.fill(angles, values, alpha=0.25, color='#2E86AB')
        
        # 设置刻度标签
        ax.set_xticks(angles[:-1])
        ax.set_xticklabels(categories, fontsize=12)
        
        # 设置y轴刻度
        ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
        ax.set_yticklabels(['20%', '40%', '60%', '80%', '100%'], fontsize=10)
        ax.set_ylim(0, 1)
        
        # 添加网格
        ax.grid(True, alpha=0.3)
        
        # 添加标题
        reader_name = basic_info.get('name', f'读者{profile.get("basic_info", {}).get("reader_id", "未知")}')
        plt.title(f'{reader_name} - 阅读画像雷达图', size=16, pad=20)
        
        # 添加图例
        plt.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
        
        # 显示原始值标注
        for i, (angle, value, raw_val) in enumerate(zip(angles[:-1], values[:-1], raw_values)):
            if i < len(categories):
                ax.annotate(f'{raw_val:.1f}', 
                           xy=(angle, value), 
                           xytext=(5, 5), 
                           textcoords='offset points',
                           fontsize=9)
        
        plt.tight_layout()
        
        # 保存或显示图表
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
            print(f"图表已保存至: {save_path}")
        
        return fig, raw_values
    
    def visualize_reader_profile(self, reader_id, save_path=None):
        """
        主函数:完整的读者画像可视化流程
        """
        try:
            # 1. 获取读者画像数据
            print(f"正在获取读者 {reader_id} 的画像数据...")
            profile = self.get_reader_profile(reader_id)
            
            # 2. 打印基本信息
            self.print_profile_summary(profile)
            
            # 3. 创建雷达图
            print("正在生成雷达图...")
            fig, values = self.create_radar_chart(profile, save_path)
            
            # 4. 显示图表
            plt.show()
            
            return {
                'profile': profile,
                'figure': fig,
                'metrics': values
            }
            
        except Exception as e:
            logging.error(f"可视化过程中出错: {e}")
            raise
    
    def print_profile_summary(self, profile):
        """打印读者画像摘要"""
        metrics = profile['reading_metrics']
        basic_info = profile['basic_info']
        
        print("\n" + "="*50)
        print(f"读者画像摘要 - {basic_info.get('name', '未知读者')}")
        print("="*50)
        print(f"📚 总阅读量: {metrics['total_books']} 本书")
        print(f"⏱️  平均阅读时长: {metrics['avg_borrow_duration']:.1f} 天")
        print(f"🚀 阅读速度: {basic_info.get('reading_speed', 0):.1f} 页/天")
        print(f"🏷️  兴趣主题: {metrics['unique_topics']} 个")
        print(f"✅ 阅读完成率: {metrics['completion_rate']:.1f}%")
        print(f"🎯 兴趣领域: {', '.join(profile['interests'][:5])}{'...' if len(profile['interests']) > 5 else ''}")
        print("="*50 + "\n")
    
    def close(self):
        """关闭数据库连接"""
        self.driver.close()

# 使用示例
def main():
    # 初始化可视化器
    visualizer = ReaderProfileVisualizer(
        neo4j_uri="bolt://localhost:7687",
        username="neo4j",
        password="password"
    )
    
    try:
        # 可视化读者画像
        reader_id = "R2024001"
        result = visualizer.visualize_reader_profile(
            reader_id=reader_id,
            save_path=f"reader_{reader_id}_profile.png"
        )
        
        print("读者画像可视化完成!")
        
    except Exception as e:
        print(f"错误: {e}")
    
    finally:
        visualizer.close()

if __name__ == "__main__":
    main()

七、欢迎纠错

  欢迎纠错,随时更新。
  联系方式:评论、私信,或 企鹅 :179 0042 182 。
  码字不易,如觉得还可以,请给个免费的 zan 和 soucang ,让我有动力继续写下去。

八、免费爬虫

https://affiliate
.bazhuayu
.com
/M8lKUC

九、论文写作/Python 学习智能体

https://chatglm.cn/share/WF2C5ree


  • 以下关于 Markdown 编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

++ 新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

++ 功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

++ 合理的创建标题,有助于目录的生成

直接输入1次+,并按下space后,将生成1级标题。
输入2次+,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

++ 如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

++ 插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

++ 如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

++ 生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

++ 创建一个表格
一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

+++ 设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

+++ SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

++ 创建一个自定义列表
Markdown
: Text-to-HTML conversion tool

Authors
John
Luke

++ 如何创建一个注脚

一个具有注脚的文本。2

++ 注释也是必不可少的

Markdown将文本转换为 HTML

++ KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

++ 新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

++ UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

++ FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

++ 导出与导入

+++ 导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

+++ 导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值