【论文复现】基于文本挖掘的互联网医疗平台用户画像模型构建

 

🤵‍♂️ 个人主页:@艾派森的个人主页

✍🏻作者简介:Python学习者
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+


目录

1.项目背景

2.项目介绍

2.1文献来源

2.2数据集介绍

3.技术工具

4.实验过程

4.1导入数据

4.2数据预处理

4.3数据可视化

4.4LDA主题分析

4.5Kmeas聚类

源代码


1.项目背景

随着互联网技术的飞速发展,特别是大数据、人工智能等技术的不断成熟,互联网医疗平台已成为连接医患双方的重要桥梁。这些平台通过提供在线问诊、健康咨询、电子病历管理等服务,极大地提升了医疗服务的便捷性和效率。然而,互联网医疗平台在快速发展的同时,也面临着诸多挑战,如用户描述的模糊性、医生远程诊疗的困难性、以及医患之间沟通障碍等。

在这些挑战中,用户描述的模糊性尤为突出。由于患者对自身病情的理解有限,或受表达能力影响,他们提供的病情描述往往不够准确和详细,这给医生的远程诊疗带来了极大的困扰。为了提高诊疗的准确性和效率,优化患者的就诊体验,构建一个能够准确反映用户需求和特点的画像模型显得尤为重要。

用户画像作为一种有效勾画需求客户、准确联系用户诉求和创造方向的工具,已经在多个领域得到了广泛应用。它通过收集和分析用户的行为数据、偏好信息等,形成对用户群体的精准描述,从而为产品和服务的定制化提供有力支持。在互联网医疗领域,用户画像模型同样具有重要意义。它可以帮助平台更好地理解用户需求,优化服务流程,提升服务质量,最终实现医患双方的共赢。

基于此,本研究旨在利用文本挖掘技术,构建基于互联网医疗平台的用户画像模型。具体而言,本研究将以自闭症疾病问诊数据为例,通过Python爬虫技术获取好大夫在线医疗平台的问诊数据,并运用隐含狄利克雷分布(LDA)与Kmeans结合的模型对数据进行划分和降维聚类,实现用户群体的分类并构建出具有代表性的用户画像。

本研究的意义在于,通过构建用户画像模型,可以深入挖掘用户的问诊主题和需求特点,为平台优化问诊填写模板、提高用户填写疾病描述的准确性、提升问诊效率和患者满意度提供有力支持。同时,该研究也有助于推动互联网医疗平台向更加智能化、个性化的方向发展,为医患双方提供更加便捷、高效的医疗服务。

2.项目介绍

2.1文献来源

[1]吕艳华,王康龙,钟小云,等.基于文本挖掘的互联网医疗平台用户画像模型构建[J].医学信息学杂志,2024,45(06):7-12.

2.2数据集介绍

        本实验数据集来源于好大夫在线医疗平台,使用Python爬虫采集孤独症(自闭症)的问诊数据,包括患者性别、年龄、问诊内容等,共计爬取400页问诊数据,累计6000条数据。

 部分爬虫代码如下:(完整版请关注文末公主号后进q群领取)

import requests
from lxml import etree
import csv
import time
import random

def main(page):
    cookies = {
        'acw_tc': 'ddcc42ac17264803542016009e2feafef31e19332b0823318854f787b4',
        'g': '46152_1726480354852',
        'Hm_lvt_dfa5478034171cc641b1639b2a5b717d': '1726480356',
        'HMACCOUNT': 'CE22B48046061130',
        'g': 'HDF.84.66e60d45f038e',
        'Hm_lpvt_dfa5478034171cc641b1639b2a5b717d': '1726480587',
    }

    headers = {
        'accept': '*/*',
        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'cache-control': 'no-cache',
        'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        # 'cookie': 'acw_tc=ddcc42ac17264803542016009e2feafef31e19332b0823318854f787b4; g=46152_1726480354852; Hm_lvt_dfa5478034171cc641b1639b2a5b717d=1726480356; HMACCOUNT=CE22B48046061130; g=HDF.84.66e60d45f038e; Hm_lpvt_dfa5478034171cc641b1639b2a5b717d=1726480587',
        'origin': 'https://www.haodf.com',
        'pragma': 'no-cache',
        'priority': 'u=1, i',
        'referer': 'https://www.haodf.com/citiao/jibing-zibizheng/bingcheng.html?p=4',
        'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-origin',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
        'x-requested-with': 'XMLHttpRequest',
    }

    data = {
        'nowPage': page,
        'pageSize': '15',
        'diseaseId': '18',
    }

    response = requests.post('https://www.haodf.com/ndisease/ajaxLoadMoreWenzhen', cookies=cookies, headers=headers, data=data)
    html_text = response.json()['data']['list']
    html_text = f'<ul>{html_text}</ul>'
    tree = etree.HTML(html_text)
    li_list = tree.xpath('//ul/li')
    for li in li_list:
        info1 = li.xpath('./a/div/span[1]/text()')[0]
        sex = info1.split('  ')[0].split(':')[1]
        age = info1.split('  ')[1]
        text = li.xpath('./a/h3/text()')[0].replace('\n','')
        talk_num = li.xpath('./div/div[1]/span[1]/span[1]/text()')[0]
        doctor = li.xpath('./div/div[2]/a[1]/text()')[0]
        hospital = li.xpath('./div/div[2]/a[2]/text()')[0]
        department = li.xpath('./div/div[2]/a[3]/text()')[0]
        print(sex,age,text,talk_num,doctor,hospital,department)

3.技术工具

Python版本:3.9

代码编辑器:jupyter notebook

4.实验过程

4.1导入数据

导入第三方库并加载数据集

查看数据大小

查看数据基本信息

4.2数据预处理

统计数据缺失值情况

统计数据重复值情况

发现有一个重复数据,删除即可

处理年龄变量,这里自定义一个函数,基于患者年龄分为 6 个类别: 1 (0 ~ 3 岁)、 2 (4 ~ 6 岁)、 3 (7 ~ 9 岁)、 4 (10 ~ 14 岁)、 5 (15~ 19 岁)、 6 (20岁以上) 

再次统计缺失值,因为上一步在处理年龄变量的时候将异常数据设置为空值

删除即可

最终还剩5903条有效数据

处理性别变量,性别分为两类: 1 (男)、 2 (女)。

4.3数据可视化

查看性别比例

查看年龄分布

4.4LDA主题分析

首先自定义一个中文分词函数,在分词过程中剔除 “今天” “呢” “吗” 等无意义词汇, 将 “利培酮” “肠道菌群” “粪便移植” 等词添入字典提升分词准 确性。

调用函数进行分词 

通常模型分类最优主题数根据困惑度大小决定, 但容易受噪声词汇干扰 , 因此采用主题一致性衡量主题数, 考虑主题中词汇之间相关性, 使主题质量评估更符合公众对主题的理解 。

做出主题一致性曲线

由上图可知,当主题数为 11 时, 一致性分数最大, 因此设定主题数为 11, 得出每个主题的主要内容。

令k=11训练lda主题模型

得到每个主题的特征词及其权重 

<Basic Info>
| LDAModel (current version: 0.12.7)
| 5898 docs, 64809 words
| Total Vocabs: 8099, Used Vocabs: 1716
| Entropy of words: 6.32534
| Entropy of term-weighted words: 6.32534
| Removed Vocabs: <NA>
|
<Training Info>
| Iterations: 10, Burn-in steps: 0
| Optimization Interval: 10
| Log-likelihood per word: -7.38909
|
<Initial Parameters>
| tw: TermWeight.ONE
| min_cf: 0 (minimum collection frequency of words)
| min_df: 5 (minimum document frequency of words)
| rm_top: 0 (the number of top words to be removed)
| k: 11 (the number of topics between 1 ~ 32767)
| alpha: [0.1] (hyperparameter of Dirichlet distribution for document-topic, given as a single `float` in case of symmetric prior and as a list with length `k` of `float` in case of asymmetric prior.)
| eta: 0.01 (hyperparameter of Dirichlet distribution for topic-word)
| seed: 555 (random seed)
| trained in version 0.12.7
|
<Parameters>
| alpha (Dirichlet prior on the per-document topic distributions)
|  [0.14759663 0.14284831 0.15131514 0.15687634 0.1418712  0.15016295
|   0.14765388 0.15163101 0.14869046 0.14630498 0.13527387]
| eta (Dirichlet prior on the per-topic word distribution)
|  0.01
|
<Topics>
| #0 (5967) : 孩子 发育 医生 迟缓 障碍
| #1 (5693) : 孩子 斯伯格 医生 焦虑 情绪
| #2 (5912) : 孩子 治疗 交流 说话 医生
| #3 (7354) : 发育 语言 迟缓 障碍 检查
| #4 (5417) : 孩子 情绪 说话 上学 治疗
| #5 (6126) : 孩子 语言 说话 宝宝 发育
| #6 (5804) : 孩子 情绪 医生 癫痫 发育
| #7 (6104) : 孩子 语言 发育 迟缓 不好
| #8 (5663) : 孩子 说话 宝宝 交流 一岁
| #9 (5517) : 孩子 情况 发育 喜欢 迟缓
| #10 (5252) : 孩子 障碍 谱系 语言 耐受
|

LDA主题可视化

 关于主题对应内容,大家可参考文献中的分析并结合自己的结果,得出主题对应内容

4.5Kmeas聚类

首先得出每个主题对应的标签

from gensim import corpora
data_set=[]  #建立存储分词的列表
for i in range(len(df['content_cutted'].to_list())):
    result=[]
    seg_list = df['content_cutted'].to_list()[i].split()
    for w in seg_list :  #读取每一行分词
        result.append(w)
    data_set.append(result)

dictionary = corpora.Dictionary(data_set)  # 构建词典
corpus = [dictionary.doc2bow(text) for text in data_set]  #表示为第几个单词出现了几次
from gensim.models.ldamodel import LdaModel
lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=11, passes = 30,random_state=1)
label_list = [] 
for i in lda.get_document_topics(corpus)[:]:
    listj=[]
    for j in i:
        listj.append(j[1])
    bz=listj.index(max(listj))
    label_list.append(i[bz][0])
df['label'] = label_list
df.head()

使用手肘法碎石图选定最优聚类数 

 当 K > 3 时, SSE 的下降幅度放缓, 整图在此形成 “手肘” 状,  因此确定 K = 3 时 为最优聚类效果。

训练模型得出聚类标签

根据聚类结果,并使用词云图做出画像(词云图代码如需要请参考以前的文章)

本次论文技术复现到这里结束! 复现的内容并不是很完整!如需源码或数据集请关注公主号【派森小木屋】!

源代码

import pandas as pd
import matplotlib.pylab as plt
import numpy as np
import seaborn as sns
sns.set(font='SimHei')
plt.rcParams['font.sans-serif'] = ['SimHei'] #解决中文显示
plt.rcParams['axes.unicode_minus'] = False   #解决符号无法显示
import warnings
warnings.filterwarnings('ignore')

df = pd.read_csv('autism_inquiry_data.csv')
df.head()
df.shape
df.info()
df.isnull().sum()
df.duplicated().sum()
df.drop_duplicates(inplace=True)
# 自定义一个处理年龄变量的函数
def dispose_age(x):
    try:
        if x[1] == '个':
            return 1 # 0-3岁返回1
        elif x[1] == '岁':
            if 0 < int(x[0]) < 4:
                return 1 # 0-3岁返回1
            elif 4 <= int(x[0]) < 7:
                return 2 # 4-6岁返回2
            elif 7<= int(x[0]) < 10:
                return 3 # 7-9岁返回3
        elif x[2] == '岁':
            if 10<= int(x[:2]) <15:
                return 4 # 10-14岁返回4
            elif 15<= int(x[:2]) <20:
                return 5 # 15-19岁返回5
            elif 20<= int(x[:2]):
                return 6 # 20岁以上返回6
    except:
        return None # 不满足的异常数据设置为空值


df['年龄'] = df['年龄'].apply(dispose_age)
df['年龄']
df.isnull().sum()
df.dropna(inplace=True)
df.shape
# 编码 男为1,女为2
df['性别'] = df['性别'].apply(lambda x:1 if x=='男' else 2) 
df.head()
y = df['性别'].value_counts().values.tolist()
plt.pie(y,
        labels=['男','女'], # 设置饼图标签
        colors=["#d5695d", "#5d8ca8"], # 设置饼图颜色
        explode=(0, 0.1), # 第二部分突出显示,值越大,距离中心越远
        autopct='%.2f%%', # 格式化输出百分比
       )
plt.title("男女比例")
plt.show()
sns.countplot(data=df,x='年龄')
plt.xticks(range(6),['0~3岁','4~6岁','7~9岁','10~14岁','15~19岁','20岁以上'])
plt.title('年龄分布')
plt.show()
import re
import jieba

# 自定义分词函数 
def chinese_word_cut(mytext):
    jieba.load_userdict('dic.txt')  # 这里你可以添加jieba库识别不了的新词,避免将一些新词拆开,例如将 “利培酮” “肠道菌群” “粪便移植” 等词添入字典提升分词准确性。
    jieba.initialize()
    # 文本预处理 :去除一些无用的字符只提取出中文出来
    new_data = re.findall('[\u4e00-\u9fa5]+', mytext, re.S)
    new_data = " ".join(new_data)
 
    # 文本分词
    seg_list_exact = jieba.cut(new_data)
    result_list = []
    with open('停用词库.txt', encoding='utf-8') as f: # 可根据需要打开停用词库,然后加上不想显示的词语
        con = f.readlines()
        stop_words = set()
        for i in con:
            i = i.replace("\n", "")   # 去掉读取每一行数据的\n
            stop_words.add(i)
 
    for word in seg_list_exact:
        if word not in stop_words and len(word) > 1:
            result_list.append(word)      
    return " ".join(result_list)

# 中文分词
df["content_cutted"] = df['问诊内容'].apply(chinese_word_cut)
df.head()
import tomotopy as tp
 
def find_k(docs, min_k=1, max_k=20, min_df=2):
    # min_df 词语最少出现在2个文档中
    scores = []
    for k in range(min_k, max_k):
        mdl = tp.LDAModel(min_df=min_df, k=k, seed=555)
        for words in docs:
            if words:
                mdl.add_doc(words)
        mdl.train(20)
        coh = tp.coherence.Coherence(mdl)
        scores.append(-coh.get_score())
 
    plt.plot(range(min_k, max_k), scores)
    plt.xlabel("number of topics")
    plt.ylabel("coherence")
    plt.show()
    
find_k(docs=df['content_cutted'], min_k=1, max_k=14, min_df=5)
import tomotopy as tp
# 初始化LDA
mdl = tp.LDAModel(k=11, min_df=5, seed=555)
for words in df['content_cutted']:
    #确认words 是 非空词语列表
    if words:
        mdl.add_doc(words=words.split())

#训 练
mdl.train()
# 查看每个topic feature words
wordcloud_words = []
for k in range(mdl.k):
    with open(f'主题{k}特征词及其权重.txt','w',encoding='utf-8')as f:
        print('Top 20 words of topic #{}'.format(k))
        print(mdl.get_topic_words(k, top_n=25))
        for item in mdl.get_topic_words(k, top_n=50): # 将主题的前50个特征词及其权重进行保存
            f.write(str(item[0]))
            f.write('\t')
            f.write(str(item[1]))
            f.write('\n')
# 查看话题模型信息
mdl.summary()
import pyLDAvis
import numpy as np
 
# 在notebook显示
pyLDAvis.enable_notebook()
 
# 获取pyldavis需要的参数
topic_term_dists = np.stack([mdl.get_topic_word_dist(k) for k in range(mdl.k)])
doc_topic_dists = np.stack([doc.get_topic_dist() for doc in mdl.docs])
doc_topic_dists /= doc_topic_dists.sum(axis=1, keepdims=True)
doc_lengths = np.array([len(doc.words) for doc in mdl.docs])
vocab = list(mdl.used_vocabs)
term_frequency = mdl.used_vocab_freq
 
prepared_data = pyLDAvis.prepare(
    topic_term_dists, 
    doc_topic_dists, 
    doc_lengths, 
    vocab, 
    term_frequency,
    start_index=0, # tomotopy话题id从0开始,pyLDAvis话题id从1开始
    sort_topics=False # 注意:否则pyLDAvis与tomotopy内的话题无法一一对应。 
)
 
# 可视化结果存到html文件中
pyLDAvis.save_html(prepared_data, 'ldavis.html')
 
# notebook中显示
pyLDAvis.display(prepared_data)
from gensim import corpora
data_set=[]  #建立存储分词的列表
for i in range(len(df['content_cutted'].to_list())):
    result=[]
    seg_list = df['content_cutted'].to_list()[i].split()
    for w in seg_list :  #读取每一行分词
        result.append(w)
    data_set.append(result)

dictionary = corpora.Dictionary(data_set)  # 构建词典
corpus = [dictionary.doc2bow(text) for text in data_set]  #表示为第几个单词出现了几次
from gensim.models.ldamodel import LdaModel
lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=11, passes = 30,random_state=1)
label_list = [] 
for i in lda.get_document_topics(corpus)[:]:
    listj=[]
    for j in i:
        listj.append(j[1])
    bz=listj.index(max(listj))
    label_list.append(i[bz][0])
df['label'] = label_list
df.head()
from sklearn.cluster import KMeans
new_df = df[['性别','年龄','label']]
# 肘部法则
loss = []
for i in range(2,10):
    model = KMeans(n_clusters=i).fit(new_df)
    loss.append(model.inertia_)
    
plt.plot(range(2,10),loss)
plt.xlabel('k')
plt.ylabel('loss')
plt.show()
k=3
kmodel=KMeans(n_clusters=k)#创建聚类模型
kmodel.fit(new_df)#训练模型
r1=pd.Series(kmodel.labels_).value_counts()
r2=pd.DataFrame(kmodel.cluster_centers_)
r=pd.concat([r2,r1],axis=1)
r.columns=list(new_df.columns)+[u'聚类数量']
r3=pd.Series(kmodel.labels_,index=new_df.index)#类别标记
r=pd.concat([new_df,r3],axis=1)#数据合并
r.columns=list(new_df.columns)+[u'聚类类别']
r
r.groupby(by=['聚类类别','性别'])['性别'].count()
r.groupby(by=['聚类类别','年龄'])['性别'].count()
r.groupby(by=['聚类类别','label'])['性别'].count()

资料获取,更多粉丝福利,关注下方公众号获取

在这里插入图片描述

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾派森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值