Python 机器学习 基础 之 【实战案例】中药数据分析项目实战
目录
Python 机器学习 基础 之 【实战案例】中药数据分析项目实战
一、简单介绍
二、中药数据分析项目实战
三、数据处理与分析实战
1、数据读取
2、中药材数据集的数据处理与分析
2.1数据清洗
2.2、 提取别名
3、提取药方成分
4、挖掘常用药物组合
四、小结
附录
一、代码地址
二、文中的关联图(带权有向图)简单绘制代码参考
一、简单介绍
Python是一种跨平台的计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。Python是一种解释型脚本语言,可以应用于以下领域: Web 和 Internet开发、科学计算和统计、人工智能、教育、桌面界面开发、软件开发、后端开发、网络爬虫。
Python 机器学习是利用 Python 编程语言中的各种工具和库来实现机器学习算法和技术的过程。Python 是一种功能强大且易于学习和使用的编程语言,因此成为了机器学习领域的首选语言之一。Python 提供了丰富的机器学习库,如Scikit-learn、TensorFlow、Keras、PyTorch等,这些库包含了许多常用的机器学习算法和深度学习框架,使得开发者能够快速实现、测试和部署各种机器学习模型。
Python 机器学习涵盖了许多任务和技术,包括但不限于:
- 监督学习:包括分类、回归等任务。
- 无监督学习:如聚类、降维等。
- 半监督学习:结合了有监督和无监督学习的技术。
- 强化学习:通过与环境的交互学习来优化决策策略。
- 深度学习:利用深度神经网络进行学习和预测。
通过 Python 进行机器学习,开发者可以利用其丰富的工具和库来处理数据、构建模型、评估模型性能,并将模型部署到实际应用中。Python 的易用性和庞大的社区支持使得机器学习在各个领域都得到了广泛的应用和发展。
二、中药数据分析项目实战
接下来,本文将深入探讨如何运用Python在中医药领域的数据挖掘与分析。通过一系列数据处理技术,本文不仅将揭示中药材、中成药和中药方剂的内在联系和特性,还将展示如何从大量复杂的数据中提取有价值的信息。以下是对原文的丰富和扩展:
-
中药材数据集的数据清洗与探索:文章首先将介绍如何使用Python进行中药材数据集的清洗工作,包括去除重复记录、处理缺失值和异常值。随后,将引导读者如何探索性分析数据集,识别中药材的基本属性和分布特征。
-
文本处理与自然语言处理:在文本处理部分,将展示如何应用分词技术来处理中医文献和方剂说明,这有助于理解中医药的用法和配伍规律。通过自然语言处理技术,可以更好地解析和利用非结构化的文本数据。
-
关联规则分析算法的应用:文章将重点介绍关联规则分析,这是一种发现变量之间有趣关系(频繁项集和关联规则)的方法。在中医药领域,此技术可以用来分析不同药材之间的组合规律和用药习惯。
-
构建中药材词典:通过数据挖掘技术,本文将指导读者如何构建一个包含中药材名称、属性、功效等信息的词典,为中医药研究和应用提供基础数据支持。
-
中成药和中药方剂的药物构成分析:本文将分析中成药和中药方剂的组成,揭示它们的配伍原理和治疗逻辑,帮助读者理解中医药方的复杂性和精妙性。
-
特殊字段的数据获取与过滤:文章将教授读者如何根据特定的字段或条件来获取和过滤数据,例如根据药材的性味或归经来筛选相关的中药材或方剂。
-
过滤后数据的关联规则分析:最后,将展示如何对过滤后的数据应用关联规则分析,以发现特定的用药模式和潜在的临床应用价值。
通过本文的学习,读者将能够综合运用数据清洗、数据处理和数据挖掘技术,不仅在中医药领域,也能在其他领域进行有效的数据分析和知识发现。我们希望读者能够通过本文,提升自己的数据处理能力,并在实际工作中发挥数据挖掘技术的价值。
中药是一种统称,凡是以传统中医理论指导采集、炮制、制剂,指导临床应用的药物都可以称为中药。中药来源宽泛,包含植物药、动物药、矿物药、化学制药、生物制药。方剂指中医治病的药方。中成药指以中药材为原料,依照中医理论制造的药物。随着时代的发展,越来越多的中医传统方剂不断被发掘。同时,根据中药理论,不断地有新的中成药被制作。中成药与传统方剂都是依照传统中医理论制作的,虽然药物制备方法与中药环境有所差别,但中药材的基本成分与组合搭配却不会发生实质性的变化。另外,除单个中药材外,不同中药材的组合往往也是中药是否能够发挥作用的关键。通过分析中药方剂与中成药的药方,可以得到常用的药材组合,从而可以为新药研究与中药的临床应用提供巨大的医学价值。同时,通过比对中成药与传统中药方剂之间的中药材使用区别,也可以为现代中成药的制作提供巨大的参考价值。
本项目提供了中药材和中成药两种数据。本章中,我们将把事先获取的数据与机器学习和数据挖掘技术相结合,完成以下两个研究目标:
● 清理原始数据,获得中成药与方剂的药材构成。
● 分析中成药中的常用药物组合,并根据组合的具体数据提出有利于研究的药物组合。
三、数据处理与分析实战
中药的种类有很多,除常用的一般中药材外,传统方剂中有时也会包含其他特殊的药材。因此,获取中成药与方剂的药材构成:
第一个研究目标为:清理原始数据,获得中成药与方剂的药材构成。对于这一研究目标,我们需要使用文本处理与自然语言处理方法,从中成药与方剂的有关数据中提取有效数据。由于药材本身具有别名,因此需要构建有关的药物词典,避免将一种药材当作多种药材的错误。在词典构建后即可使用分词、数据清洗等一般方法创建分词链表,然后进行中药材的提取。
第二个研究目标为:分析中成药中的常用药物组合,并根据组合的具体数据提出有利于研究的药物组合。在第一个研究目标完成后,即可得到常用药物组合,之后则利用数据挖掘中有关关联规则分析的算法,获取常用药物组合。药物组合获取后,对于其中较为复杂的药物组合网络,可以利用知识图谱相关技术进行分析。
1、数据读取
原始数据为两个CSV表格,直接使用Pandas库读取有关数据。读取数据后,可以调用head方法查看前10条数据,调用describe方法查看常用数据描述。不过本项目数据皆为文本数据,因此无法查看最大值、最小值、标准差等基本统计数据。
查看数据代码如下:
import pandas as pd
# 导入pandas库,并将其简称为pd。Pandas是一个强大的数据分析和操作库,常用于数据预处理。
df_name = pd.read_csv("./data/name.csv", header=0)
# 使用pandas的read_csv函数读取当前目录下data文件夹中的name.csv文件。
# header=0指定第一行作为列名(header)。
# 读取的数据被存储在DataFrame对象df_name中。DataFrame是pandas中用于存储表格数据的主要数据结构。
df_name.head(10)
# 调用df_name的head方法并传入参数10,这将返回DataFrame的前10行。
# head方法用于快速查看DataFrame的开始部分,是一个便捷的方式进行数据的初步检查。
# 这里的输出将显示df_name DataFrame的前10行数据。
2、中药材数据集的数据处理与分析
2.1数据清洗
首先需要提取的数据为中药材数据集中的药名,药名存在于desc中,desc中的具体字段为:中药名、别名、英文名、药用部位、植物形态、药材性状、产地分布、采收加工、性味归经、临床应用、功效与作用、使用禁忌、相关药方、化学成分、药理研究。部分数据并不包含所有的数据种类。由研究目标可知,本次数据中可能会直接使用的为:中药名、别名、临床应用、功效与作用、化学成分。药理研究与植物形态、相关药方等内容,则可能在进行有关分析时有用。
由于desc中并非直接分类好的数据,而是将所有数据都作为文本数据存储,且存在大量空格和其他错误字符,因此,需要使用Python建立有关过滤规则进行过滤。展示有关数据进行初步查看可以发现,数据开头与结尾有空格,且存在大量名为“\u3000”的字符,这两类问题使用strip与replace方法进行过滤。
过滤后的数据依旧为字符串数据,不同数据说明依靠特殊字符“【】”进行识别,因此这里利用特殊字符进行分隔,分隔后的格式为“字段名1,内容1,字段名2,内容2,...,字段名n,内容n”。分隔完成后,利用字符查询与list的index方法,定位和获取所需的数据。别名获取的具体策略为首先使用字符串“【别名】”与list的定位索引方法index,获取它在list中的索引,然后将获得索引加1,获得其内容。
代码如下:
# 从df_name DataFrame中选择名为"desc"的列(假设这一列包含描述性文本)。
# 然后通过索引[0]选择这一列的第一行数据。
# .strip()方法用于移除字符串开头和结尾的空白字符,这包括空格、制表符、换行符等。
# 这通常用于清理数据,确保分析时不会因为无意义的空白字符影响结果。
# .replace('\u3000', '')方法用于将字符串中的特定字符替换掉。
# 这里替换的是Unicode编码\u3000,这是一个空格字符,常用于某些文档格式中表示较长的空间。
# 通过替换这个字符,进一步清理了文本数据,移除了可能的格式干扰。
string_clear = df_name["desc"][0].strip().replace('\u3000', '')
# 最终,string_clear变量将包含"desc"列第一行数据,已经过移除首尾空白和特定空格字符的清理。
string_clear
过滤后desc中的某条数据结果如下:
这段代码的目的是清洗DataFrame中"desc"列的第一行文本数据。首先,通过索引选择特定的数据行,然后使用strip
方法清除字符串两端的空白字符,最后使用replace
方法移除文本中的特定空格字符,得到一个清理后的字符串存储在变量string_clear
中。这样的清洗步骤有助于提高后续文本分析或处理的准确性。
使用特殊字符分隔,代码如下:
list_clear = string_clear.replace('【', '|【').replace('】', '】|').split("|")
# 这行代码执行了多个字符串操作:
# 1. 使用replace方法将所有的'【'替换为'|【'。
# 2. 再次使用replace方法将所有的'】'替换为'】|'。
# 这样做的目的是在原有的括号字符前或后添加一个分隔符'|',以便能够更清晰地分割字符串。
# 3. 最后,使用split方法以'|'作为分隔符将更新后的字符串拆分成一个列表。
# 这将导致所有被'|'包围的部分成为list_clear列表的元素。
list_clear.remove('')
# 这行代码调用了列表list_clear的remove方法,目的是移除列表中的空字符串元素。
# 在split操作后,如果原始字符串中存在连续的分隔符'|',可能会生成空字符串。
# 通过remove方法,确保清理掉这些无用的空字符串元素,留下非空的、有意义的部分。
list_clear
# 这行代码本身不执行操作,它只是引用了变量list_clear。
# 通常在Python中,单独引用变量会将其打印出来,或者用于后续的代码逻辑中。
# 在这个上下文中,list_clear变量包含了清理和分割后的字符串列表。
分隔后形成一个由不同属性及其对应值组成的列表,结果如下:
['【中药名】', '夏天无 xiatianwu', '【别名】', '一粒金丹、洞里神仙、飞来牡丹、土元胡、野延胡、伏地延胡索、无柄紫堇、落水珠。', '【英文名】', 'Corydalis Decumbentis Rhizoma。', '【药用部位】', '罂粟科植物伏生紫堇Corydatis decumbens (Thunb.) Pers.的块茎。', '【植物形态】', '多年生草本,全体无毛。块茎近球形,表面黑色,着生少数须根。茎细弱,丛生,不分枝。基生叶具长柄,叶片三角形,2回三出全裂,末回裂片具短柄,通常狭倒卵形;茎生叶2~3片,生茎下部以上或上部,形似基生叶,但较小,具稍长柄或无柄。总状花序顶生;苞片卵形或阔披针形.全缘;花淡紫红色,筒状唇形,上面花瓣边近圆形,先端微凹,矩圆筒形,直或向上微弯;雄蕊6,呈两体。蒴果线形,2瓣裂。种子细小。花期4~5月,果期5~6月。', '【产地分布】', '生于丘陵、低山坡或草地。喜生于温暖湿润、向阳、排水良好、土壤深厚的沙质地。分布于安徽、江苏、浙江、江西等地。', '【采收加工】', '春至初夏采块茎,去泥,洗净,晒干或鲜用。', '【药材性状】', '类球形、长圆形或不规则块状,长0.5~2厘米,直径0.5~1.5厘米。表面土黄色,棕色或暗绿色,有细皱纹,常有不规则的瘤状突起及细小的点状须根痕。质坚脆,断面黄白色或黄色,颗粒状或角质样,有的略带粉性。气无,味极苦。以个大、质坚、断面黄白者为佳。', '【性味归经】', '性温,味苦、微辛。归肝经。', '【功效与作用】', '活血、通络、行气止痛。属活血化瘀药下属分类的活血止痛药。', '【临床应用】', '用量5~16克,煎汤内服;或研末,1~3g;亦可制成丸剂。用治中风偏瘫、小儿麻痹后遗症、坐骨神经痛、风湿性关节炎、跌打损伤、腰肌劳损等。', '【药理研究】', '可引起动物产生“僵住症”,表现为木僵、嗜睡、肌肉僵硬,如随意改变其位置,可保持于该种姿势。药理实验表明,本品有抗张血管、抗血小板聚集、镇痛、解痉、降血压、松弛回肠平滑肌等作用。夏天无注射液在临床上治疗高血压脑血管病、骨关节肌肉疾病及青年近视等,均见良效。', '【化学成分】', '含四氢巴马亭(即延胡索乙素)、原阿片碱、盐酸巴马汀、空褐鳞碱、藤荷包牡丹定碱、夏天无碱、紫堇米定碱、比枯枯灵碱、掌叶防己碱等,其总碱含量达0.98%。应用高效薄层色谱分离及薄层扫描定量,对夏天无的化学成分及含量进行比较,结果表明,其延胡索乙素含量最高。', '【使用禁忌】', '尚不明确,谨慎用药。', '【相关药方】', '①治高血压,脑瘤或脑栓塞所致偏瘫:鲜夏天无捣烂。每次大粒4~5粒,小粒8~9粒,每天1~3次,米酒或开水送服,连服3~12个月。(《浙江民间常用草药》)②治各型高血压病:a.夏天无研末冲服,每次2~4克。b.夏天无、钩藤、桑白皮、夏枯草。煎服。(江西《中草药学》)③治风湿性关节炎:夏天无粉每次9克,日2次。(江西《中草药学》)④治腰肌劳损:夏天无全草15克,煎服。(江西《中草药学》)']
这段代码的目的是处理string_clear
变量中的文本,通过特定的字符替换和分割操作,生成一个包含有意义文本片段的列表list_clear
。首先,通过替换操作在特定的中文括号字符前后添加分隔符|
,然后使用这个分隔符来分割字符串,最后移除分割后可能产生的空字符串,得到一个清理过的列表。这样的处理通常用于文本的预处理阶段,为后续的文本分析或处理做准备。
2.2、 提取别名
在定义获取策略后,编写能够提取别名的代码,并利用dataframe的apply方法将提取后的数据写入原dataframe。考虑到代码可能会在之后多处重复使用,因此进行封装,类似操作不再描述。具体代码如下:
list_clear[list_clear.index('【别名】')+1].replace("。", '').split('、')
# 这行代码执行了多个操作,目的是从list_clear中提取和处理别名部分的数据:
# 1. list_clear.index('【别名】'):使用index方法找到字符串'【别名】'在list_clear中首次出现的位置。
# 注意,index方法将返回列表中首次出现的索引,如果没有找到,会抛出ValueError。
# 2. '【别名】'+1:找到'【别名】'字符串后面紧跟的第一个元素的索引。这个索引对应的是别名信息的开始位置。
# 3. list_clear[...]:使用方括号和索引来访问list_clear列表中位于别名索引处的元素。
# 4. .replace("。", ''):对获取的别名字符串使用replace方法移除所有的中文句号“。”。
# 这通常是为了进一步清理数据,移除可能影响文本处理的标点符号。
# 5. .split('、'):使用split方法以中文顿号'、'作为分隔符来分割别名字符串。
# 这将把包含多个别名的字符串分割成一个列表,列表中的每个元素都是一个单独的别名。
# 最终,这行代码将返回一个列表,包含了经过清理和分割的别名,移除了中文句号并按中文顿号分割。
结果如下:['一粒金丹', '洞里神仙', '飞来牡丹', '土元胡', '野延胡', '伏地延胡索', '无柄紫堇', '落水珠']
这段代码的目的是处理一个已经通过特定格式标记分割得到的列表list_clear
,从中找到标记为“别名”的部分,然后对这个部分进行进一步的文本清洗和分割操作,最终得到一个包含所有别名、去除了句号并按顿号分割的列表。这样的处理有助于后续对别名数据的分析或存储。
提取别名的完整代码如下:
def apply_get_alias(x, name):
# 定义一个函数apply_get_alias,它接受两个参数:x(字符串)和name(要查找的特定名称)。
string_clear = x.strip().replace('\u3000', '')
# 清除x字符串两端的空白字符,并将全角空格符\u3000替换为空字符串,以清理字符串。
list_clear = string_clear.replace('【', '|【').replace('】', '】|').split("|")
# 将'【'替换为'|【',将'】'替换为'】|',以便在中文括号周围添加分隔符'|',然后按'|'分割字符串。
list_clear.remove('')
# 如果在分割过程中产生了空字符串,将其从list_clear列表中移除。
if '【' + name + '】' in list_clear:
# 检查经过处理的列表中是否包含特定名称的标记(例如'别名')。
return list_clear[list_clear.index('【' + name + '】') + 1].replace("。", '').split('、')
# 如果存在,找到该标记后的第一个元素(即别名部分),移除中文句号,并按中文顿号分割,返回分割后的列表。
else:
return "no_alias"
# 如果不存在指定名称的标记,返回字符串"no_alias"。
# 以元组的方式传入额外的参数
alias = '别名'
# 定义一个变量alias并赋值为'别名',这个变量将作为apply函数的额外参数。
df_name["alias"] = df_name["desc"].apply(apply_get_alias, args=(alias,))
# 对df_name DataFrame中的"desc"列应用apply_get_alias函数。
# apply方法将函数应用于"desc"列中的每个元素(即每行数据)。
# args参数是一个元组,包含了额外的参数alias,这个参数将传递给apply_get_alias函数。
df_name.head()
# 显示df_name DataFrame的前几行,通常用于检查DataFrame的结构和新生成的"alias"列的数据。
这段代码的目的是定义一个自定义函数apply_get_alias
,用于处理DataFrame中的文本数据,并提取特定名称(如别名)的信息。函数首先清除文本两端的空白字符和全角空格,然后在特定字符(如中文括号)周围添加分隔符,分割文本,并移除空字符串。接着,检查分割后的列表中是否包含特定的名称标记,如果包含,则提取并进一步处理该部分文本;如果不包含,则返回一个特定的字符串(如"no_alias")。最后,使用apply
方法将这个自定义函数应用于DataFrame的某一列,并将结果存储在新的列中。通过这种方式,可以有效地从文本数据中提取和处理所需的信息。
提取后的数据没有办法直接使用,在后续使用中发现该数据存在医学上的命名错误,或者说是“冒名顶替”的错误,这是一种常见的类似药草命名错误的现象。例如,水半夏与半夏这两种药材,它们是不同的药材,水半夏最早是因为与半夏有类似药效,所以在部分药方中被用于替换半夏。由于商业或者认知错误等原因,水半夏有时也被称为半夏,但是水半夏与半夏是不同的药物,在医学上不可混用,而这里水半夏的别名中却存在“半夏”这一错误别名。同样还存在木香与川木香等其他药草的别名错误问题。因此,这里必须对所有类似情况进行处理。经过数据探索可以发现,原数据中存在这些“冒名顶替”的药材中有两种比较特别——它们去除错误别名后,就没有其他别名了。
两对“混淆”的药材为:谷芽和稻芽、木香和川木香。查询资料可知,谷芽为粟的芽,而稻芽为稻米的芽,不是一种药材。木香为菊科植物木香的干燥根,川木香则为菊科植物川木香或灰毛川木香的干燥根,同样不是一种药材。对比发现两对药材确实为不同药草,因此过滤规则不变,但在后续编程中需注意这两个特殊的别名空值。查询别名为空值的代码如下:
for i in range(len(df_name["alias"])):
# 遍历df_name DataFrame中"alias"列的索引,i为当前索引。
list_i = list(df_name["alias"][i])
# 将"alias"列中索引为i的元素(原本是字符串)转换为列表list_i。
for j in df_name["alias"][i]:
# 对于"alias"列中索引为i的每个字符j进行遍历。
if j in list(df_name["title"]):
# 如果字符j也存在于"title"列的某个元素中("title"列的每个元素也被转换为列表),则从list_i中移除字符j。
list_i.remove(j)
# 这里假设我们不希望别名中包含药名,因为药名可能在别名中作为一部分出现。
if list_i == []:
# 如果移除所有药名字符后,别名列表为空,则打印出索引i,提示别名被过滤为空。
print("过滤后别名为空", i)
df_name["alias"][i] = list_i
# 将经过过滤(移除了药名字符的)别名列表list_i重新赋值给df_name DataFrame中"alias"列的第i个元素。
print(str(df_name["alias"]))
# 将df_name DataFrame中"alias"列的内容转换为字符串并打印出来,查看更新后的别名列。
# 下面的两行代码被注释掉了,它们不会执行:
# print(df_name["desc"][list(df_name["title"]).index('谷芽')],df_name["desc"][104])
# 这行代码的意图可能是找到"title"列中'谷芽'对应的索引,然后打印"desc"列在该索引和第104行的元素。
# print(df_name["desc"][list(df_name["title"]).index('木香')],df_name["desc"][608])
# 这行代码的意图可能是找到"title"列中'木香'对应的索引,然后打印'desc'列在该索引和第608行的元素。
运行结果为:
过滤后别名为空 104
过滤后别名为空 608
0 [一粒金丹, 洞里神仙, 飞来牡丹, 土元胡, 野延胡, 伏地延胡索, 无柄紫堇, 落水珠] 1 [辽参,海男子] 2 [常春藤, 大风藤, 假葡萄藤, 走游藤蓼, 地锦] 3 [煤参, 太白洋参, 黑洋参] 4 [五花龙骨] ... 816 [乌龙须, 黑龙须] 817 [倒吊黄花, 倒吊黄, 黄花参] 818 [五毒草, 火炭毛, 乌炭子] 819 [野芫荽, 刺芹, 香信, 番香茜, 香菜] 820 [海白菜, 石莼, 纸菜, 青菜婆] Name: alias, Length: 821, dtype: object
这段代码的目的是清洗DataFrame中"alias"列的数据,确保别名中不包含与"title"列相同的字符(可能是药名)。通过两层循环,代码遍历别名的每个字符,如果字符在药名列中也存在,则从别名中移除。如果别名在移除药名后变为空列表,代码会打印出对应的索引。最后,代码打印出更新后的"alias"列的所有内容。被注释掉的两行代码看起来是为了调试目的,用于打印特定索引位置的数据。
3、提取药方成分
分析药方中的中成药的成分,首先需要提取所需的药材名称。与药材数据类似,它同样以“字段名1,内容1,字段名2,内容2,...,字段名n,内容n”的形式存在于desc字段,因此,我们使用与之前类似的方法提取处方字段。实际编程后发现,前面的许多数据存储药方成分的字段为“处方”,而部分数据为“药方组成”。因此,我们需要稍微修改一下算法,当存在“处方”或“药方组成”字段时,我们将有关字段提取出来。
首先读取中成药的数据,代码如下:
df_zhongchengyao = pd.read_csv("./data/zhongchengyao.csv", header=0)
# 导入pandas库(通常在代码开始时已经导入,因此此处不再需要导入)。
# 使用pandas库的read_csv函数读取当前目录下的"data"文件夹中的"zhongchengyao.csv"文件。
# header=0指定第一行作为DataFrame的列名(header)。
# 读取的数据被存储在DataFrame对象df_zhongchengyao中,该对象用于后续的数据操作和分析。
df_zhongchengyao.head(10)
# 调用df_zhongchengyao DataFrame的head方法并传入参数10,这将返回DataFrame的前10行数据。
# head方法通常用于快速查看DataFrame的开始部分,帮助用户对数据进行初步的检查和理解。
# 这里的输出将显示df_zhongchengyao DataFrame的前10行,以便进行数据查看。
这段代码的目的是加载名为"zhongchengyao.csv"的CSV文件到pandas DataFrame中,并显示这个DataFrame的前10行数据,以便于进行初步的数据查看和检查。
然后通过将中药名与中药的别名合并来创建词典,再利用词典在中成药与中药方剂中提取中药。其具体思路为:使用分词工具对中成药、中药药方中的文本进行全分词,然后取交集即可。但是,由于中药名字除正式名称外还有别名存在,因此我们需要将别名统一。这里使用自定义的方法,将所有的别名转换为唯一主名称,之后取set即可得到一份只有统一名称的Python链表。对此,我们需要构建只包含唯一名称与包含所有中药材名词的两个链表,以供之后使用。代码如下:
list_name_alias = []
# 初始化一个空列表,用于存储别名数据。
for i in list(df_name["alias"]):
# 遍历DataFrame df_name中"alias"列的每一项(每一项本身是一个列表)。
list_name_alias.extend(i)
# 使用extend方法将当前别名列表中的所有元素添加到list_name_alias列表中。
list_name = list(df_name["title"])
# 将DataFrame df_name中"title"列的每个元素转换为一个列表。
list_name_all = list_name + list_name
# 创建一个新的列表list_name_all,它是list_name列表的两倍长度,通过复制自身实现。
def get_main_name(x):
# 定义一个函数get_main_name,它接受一个参数x(可能代表某个别名中的单个名称或词语)。
int_len = len(list(df_name["alias"]))
# 获取"alias"列中别名列表的长度。
list_alias = list(df_name["alias"])
# 将"alias"列的每个元素再次转换为列表。
for i in range(int_len):
# 遍历别名列表的长度。
if x in list_alias[i]:
# 如果x存在于第i个别名列表中,即x是别名的一部分。
return list_name[i]
# 返回对应于该别名的"title"列中的主名称。
def get_name(list_jieba):
# 定义一个函数get_name,它接受一个参数list_jieba(可能是一个经过分词处理的词语列表)。
union_1 = list(set(list_jieba).intersection(set(list_name)))
# 通过集合操作找出list_jieba和list_name中共有的元素,即同时出现在两者中的词语,存储在union_1中。
union_2 = list(set(list_jieba).intersection(set(list_name_alias)))
# 通过集合操作找出list_jieba和list_name_alias中共有的元素,即同时出现在别名中的词语,存储在union_2中。
if union_2 != []:
# 如果存在同时出现在别名中的词语。
for i in range(len(union_2)):
# 遍历这些共有元素。
union_2[i] = get_main_name(union_2[i])
# 使用get_main_name函数获取每个共有元素对应的主名称,并更新union_2列表中的相应元素。
return union_1+union_2
# 返回一个列表,包含union_1和union_2中的所有元素,即list_jieba中属于主名称和别名的所有词语。
这段代码的目的是处理DataFrame中的别名和标题数据,通过定义函数提取出与分词结果相关的主名称和别名。get_main_name
函数用于根据别名中的词语找到对应的主名称,而get_name
函数则用于将分词结果(list_jieba
)与主名称及别名列表进行匹配,找出相关的名称。这样的处理有助于在文本分析或数据匹配中识别和关联相关的药名或别名。
对于处方构成的获取,使用的是“循环+分词+取并”的方式,代码如下:
def apply_get_prescription(x, l_p):
# 定义一个函数apply_get_prescription,它接受两个参数:x(字符串,可能包含药方描述)和l_p(包含特定标签的列表)。
import re
# 导入正则表达式模块re,用于文本处理。
string_clear = x.strip().replace('\u3000', '')
# 清除x字符串的首尾空白字符,并替换全角空格符\u3000为空字符串。
list_clear = string_clear.replace('【','|【').replace('】','】|').split("|")
# 在'【'和'】'字符周围添加分隔符'|',然后按'|'分割字符串,生成list_clear列表。
list_clear.remove('')
# 如果分割后list_clear列表中有空字符串元素,将其移除。
for item in l_p:
# 遍历l_p列表中的每个项,这些项是我们要在文本中查找的特定标签(如'药方组成')。
if '【'+ item +'】' in list_clear:
# 如果当前项作为标签存在于list_clear中,表示找到了药方组成或处方部分。
str_clear = list_clear[list_clear.index('【'+ item +'】')+1]
# 获取标签后的文本,即药方组成或处方的具体内容。
import jieba
# 导入jieba库,用于中文分词。
jieba_cut_string = jieba.cut(str_clear,HMM=True)
# 使用jieba进行中文分词,HMM=True表示使用HMM模型进行分词。
list_jieba = "|".join(jieba_cut_string).split('|')
# 将分词结果连接为一个字符串,再用'|'分割成列表。
union = get_name(list_jieba)
# 调用get_name函数处理分词结果,该函数可能是之前定义的,用于进一步处理分词结果。
return list(set(union))
# 返回去重后的分词结果列表。
else:
# 如果遍历完l_p列表中的所有项,都没有在list_clear中找到对应的标签,则执行else块。
return "no_alias"
# 返回字符串"no_alias",表示没有找到药方组成或处方部分。
# 以元组的方式传入额外的参数
prescription = ['药方组成','处方']
# 定义一个列表prescription,包含我们要查找的特定标签。
df_zhongchengyao["prescription"] = \
df_zhongchengyao["desc"].apply(apply_get_prescription,args=(prescription,))
# 使用apply函数将apply_get_prescription应用到df_zhongchengyao DataFrame的"desc"列。
# args参数是一个元组,包含了额外的参数prescription,这个参数将传递给apply_get_prescription函数。
# 应用结果将作为新的一列"prescription"添加到df_zhongchengyao DataFrame中。
运行结果:
Building prefix dict from the default dictionary ... Loading model from cache C:\Users\xiankui.qin\AppData\Local\Temp\jieba.cache Loading model cost 0.511 seconds. Prefix dict has been built successfully.
这段代码的目的是定义一个自定义函数apply_get_prescription
,用于处理药方描述文本,提取药方组成或处方部分,并使用jieba进行中文分词。然后,使用apply
方法将这个自定义函数应用于DataFrame的某一列,并将结果存储在新的列中。通过这种方式,可以有效地从药方描述文本中提取和处理所需的信息。
以上代码实现了别名的统一,将所有的别名转换为唯一主名称,形成了Python链表。
查看 prescription 数据
df_zhongchengyao["prescription"]
# 这行代码引用了DataFrame df_zhongchengyao中的"prescription"列。
# 在pandas中,使用DataFrame的名称后跟列名(用方括号括起来)可以访问特定的列。
# 这个操作不会修改数据,它仅仅是选择了DataFrame中的一个列作为对象。
# 通常,这样的引用用于以下几种情况:
# 1. 打印或查看特定列的数据。
# 2. 将这一列的数据传递给其他函数或进行进一步的数据处理。
# 3. 作为数据操作的一部分,比如创建新的列或修改现有列。
运行结果:
0 [党参, 龟甲, 枸杞子] 1 [金银花] 2 [生姜, 黄芩, 党参, 柴胡, 甘草, 半夏, 大枣] 3 [柴胡] 4 [黄芩, 柴胡] ... 2076 [乳香, 白矾, 冰片, 玄明粉, 没药, 炉甘石, 芒硝, 硼砂, 麝香] 2077 [珍珠, 牛黄, 三七, 羚羊角] 2078 [人参, 麻黄, 桔梗, 白芍, 五味子, 苦杏仁, 甘草, 当归, 半夏, 补骨脂, 桂枝... 2079 [龟甲, 地黄, 知母, 当归, 菊花, 南沙参, 黑豆, 黄芩, 白芍, 桑叶, 地骨皮,... 2080 [冰片] Name: prescription, Length: 2081, dtype: object
这行代码本身不执行任何数据修改或输出操作,它只是访问并选择了df_zhongchengyao
DataFrame中的"prescription"
列。如果需要查看或使用这一列的数据,通常还需要进行额外的操作,比如使用head()
函数来查看前几行数据,或者将其作为参数传递给其他数据处理函数。
4、挖掘常用药物组合
接下来,分析中成药中的常用药物组合,并根据组合的具体数据,提出有利于研究的药物组合。目前我们已经获取了中成药与中药方剂的药物组成,因此需要使用对应算法(Apriori算法)对常见药物组合进行挖掘。
Apriori算法是一种常见的关联规则分析算法,得到的结果为{A} -> {B}的形式,前项A可能是一个数据,也可能是两个或多个数据的组合(之后将前面的数据项统称为前项),后项B为有关搭配的数据,同样可能是一个数据,也可能是多个数据(之后将后面的数据项统称为后项)。对关联项的挖掘,我们主要通过两个参数进行控制:min_support(最小支持度)与min_confidence(最小置信度)。支持度表示某个组合出现的次数与总次数之间的比例。支持度越高,该组合出现的频率越大。置信度则表示条件概率,如{A} -> {B},表示在A发生的这一条件下,B发生的概率是多少。在这里,最小支持度则表示所有药方中最起码要有多少出现了这类药物(或药物组合)。例如组合为{A} -> {B},置信度为1,表示存在A的时候,有多少的比例会出现B。这两个参数都是Apriori算法的核心参数。同时,如果我们需要分析常用的药物组合,那么药物本身出现的比例以及搭配药物出现的概率也是我们需要控制的变量——通过控制有关变量,可以获得不同情况下的药物组合类型。
参数的估计需要依据实际数据的比例。通过代码计算可知,药方总数为2081,药方中出现的药材一共有15680种,药材总种类则为821类,平均每种中药出现19.1次。由于药方数量较多,我们不要求过多药方中出现某种药材,因此将Apriori算法的最小支持度设置为0.03;疾病种类繁多,因此置信度则选择为0.5,也就是使用Apriori算法发现在所有药材中前项出现的比例高于3%,且前项出现后后项出现的概率高于50%的药材组合。本节通过上述两个参数设置方法,来寻找一些3种以上的药材组合。
首先,安装efficient-apriori
!pip install efficient-apriori
# 这行代码用于安装名为efficient-apriori的Python库。
# 在Jupyter Notebook或类似支持!操作符的环境中,!允许执行系统命令。
# pip是Python的包管理工具,用于安装和管理Python库。
# 执行此命令将从Python包索引(PyPI)下载并安装efficient-apriori库及其依赖项。
# 安装完成后,您可以在Python代码中导入并使用该库提供的功能。
这条命令通常在数据分析和机器学习项目中使用,用于确保所需的库已经安装在环境中,以便可以执行关联规则挖掘等任务。如果库已经安装,则此命令将检查更新;如果尚未安装,则会添加到当前Python环境的库列表中。
并统计中成药中一共出现了多少药材,代码如下:
from efficient_apriori import apriori
# 导入efficient_apriori库中的apriori函数。
# efficient_apriori是一个用于挖掘关联规则的库,apriori函数可用于从数据集中生成频繁项集。
int_tmp = 0
# 初始化一个整数变量int_tmp,用于累计计数。
for i in list(df_zhongchengyao["prescription"]):
# 遍历DataFrame df_zhongchengyao中"prescription"列的每一项。
# list函数将"prescription"列中的数据转换为列表,以便遍历。
int_tmp += len(i)
# 将当前项i的长度(即项中元素的数量)累加到int_tmp变量中。
print(int_tmp)
# 打印int_tmp的当前值,这通常用于调试或查看累计的总数。
print(int_tmp/821)
# 计算int_tmp的值除以821的结果,并打印出来。
# 这里821可能是一个特定的数值,例如数据集中记录的总数或某个特定的基数。
print(int_tmp/821/821)
# 进一步计算上述结果除以821,并打印出来。
# 这可能是为了得到某个比例或比率,具体含义取决于上下文。
15680 19.09866017052375 0.023262679866654996
这段代码的目的是遍历DataFrame中某一列的所有项,并计算所有项中元素总数的累计和。然后,代码中通过除以特定的数值(在这种情况下是821)来计算并打印出一些数值比例或比率。这样的计算可能用于数据分析或验证特定假设。
关联规则挖掘算法代码如下:
itemsets, rules = apriori(list(df_zhongchengyao["prescription"]), min_support=0.03, min_confidence=0.5)
# 使用apriori算法处理DataFrame df_zhongchengyao中的"prescription"列。
# 将"prescription"列转换为列表,作为apriori函数的输入数据。
# min_support=0.03设定了最小支持度阈值为0.03,即一个项集要被认为是频繁的,
# 它必须在所有交易中出现的频率至少为0.03。
# min_confidence=0.5设定了最小置信度阈值为0.5,即如果规则A => B的置信度大于0.5,
# 则认为在A出现的情况下,B也有很大可能跟随出现。
# 函数返回两个对象:itemsets(频繁项集)和rules(强关联规则)。
print(rules)
# 打印apriori算法找到的强关联规则。
# 关联规则通常表示为A => B形式,其中A是前提条件,B是后件。
# 这些规则可以帮助我们理解数据中的内在关系,例如在某些药方中哪些药材经常一起出现。
运行结果: [{没药} -> {乳香}, {乳香} -> {没药}, {半夏} -> {甘草}, {半夏} -> {陈皮}, {山药} -> {茯苓}, {川芎} -> {当归}, {熟地黄} -> {当归}, {白芍} -> {当归}, {红花} -> {当归}, {桔梗} -> {甘草}, {白术} -> {甘草}, {白芍} -> {甘草}, {苦杏仁} -> {甘草}, {茯苓} -> {甘草}, {薄荷} -> {甘草}, {陈皮} -> {甘草}, {麻黄} -> {甘草}, {白术} -> {茯苓}, {当归, 甘草} -> {川芎}, {川芎, 甘草} -> {当归}, {当归, 白芍} -> {川芎}, {川芎, 白芍} -> {当归}, {熟地黄, 白芍} -> {当归}, {当归, 白芍} -> {熟地黄}, {当归, 熟地黄} -> {白芍}, {甘草, 白芍} -> {当归}, {当归, 白芍} -> {甘草}, {当归, 甘草} -> {白芍}, {当归, 茯苓} -> {甘草}, {当归, 甘草} -> {茯苓}, {白芍, 茯苓} -> {当归}, {当归, 茯苓} -> {白芍}, {当归, 白芍} -> {茯苓}, {白术, 茯苓} -> {甘草}, {甘草, 茯苓} -> {白术}, {甘草, 白术} -> {茯苓}, {茯苓, 陈皮} -> {甘草}, {甘草, 陈皮} -> {茯苓}]
这段代码的目的是使用apriori算法对中药处方数据进行关联规则挖掘,找出频繁项集和强关联规则。通过设定最小支持度和最小置信度阈值,算法能够识别出在数据集中经常出现的药方组合,以及在某些药材出现时其他特定药材也很可能出现的规则。这些规则对于理解中医药方的配伍规律和药材使用习惯非常有帮助。
最后得到的常用的3种药材的中成药组合如下:
● 白术,茯苓,甘草
● 茯苓,陈皮,甘草
● 当归,白芍,川芎
● 白芍,茯苓,当归
● 当归,甘草,川芎
● 当归,茯苓,甘草
● 熟地黄,白芍,当归
● 甘草,白芍,当归
对于具体的内容,如{甘草,陈皮} -> {茯苓},其中甘草和陈皮这对组合是实际出现的,且出现比例一定高于5%,而每当该组合出现时,则有50%的可能与茯苓进行搭配。只考虑组合本身,也就是前项中的两种药材的情况,我们可以根据组合次数绘制出一个图形,具体如图12-4所示。
在图12-4中,组合次数最多的是当归,当归是所有药材组合中最重要的药材,它与白芍的连接数为4,与甘草的连接数为3,与茯苓的连接数为2,且与其他药材拥有紧密联系。其次是白芍与甘草这两种药材,它们和其他药材连接都比较紧密,且部分组合次数相对较大。再次是茯苓。最后一批组合最少的药材则是川芎、熟地黄、陈皮与白术,它们都与两种药材各组合过一次。
接下来,统计可能的搭配关系(在{甘草,陈皮} -> {茯苓},茯苓是甘草和陈皮的搭配药材,记作茯苓与甘草的关联数字为1,茯苓与陈品的关联数字为1),得到的结果如图12-5所示。
由图12-5可知,当归、甘草、白芍、茯苓这4种药材与其他药材搭配最为频繁。其次是川芎,与几种主要药材的搭配较为频繁。之后是白术、熟地黄,这两种药材与图12-4中的情况类似,都与两种主要药材有一定的搭配关系(各两次)。最后为陈皮,只和两种主要药材各有一次搭配。通过以上方法,我们得到了不同常见药材之间的辅助搭配关系。
综合上述两种情况可以得出结论:当归与白芍、甘草的直接组合较多,而甘草、当归、茯苓、川芎和白芍五味药同时也是其他药材中药的辅助用药。由于图12-4和图12-5很好地展示了各种药材之间的搭配与辅助关系,因此,也可以使用关联规则分析药物之间的搭配关系,从而得到药方,例如,当归、甘草、白芍、茯苓这一较为简单的药方组合,或者甘草、当归、茯苓、川芎、白芍、熟地黄、白术这一药材较多的药方组合。
使用类似方法同样可以得到单个药材的有关组合。由于单个药草本身出现概率就比较高,因此将算法中的参数最小置信度提高至0.7。支持度为0.03,置信度为0.7时获取药材的有关组合的实现代码如下:
itemsets, rules = apriori(list(df_zhongchengyao["prescription"]), min_support=0.03, min_confidence=0.7)
# 对DataFrame df_zhongchengyao中的"prescription"列应用apriori算法。
# 将"prescription"列转换为列表形式,作为apriori算法的输入。
# min_support=0.03设定了最小支持度阈值,意味着项集出现的最小频率为0.03(3%)。
# min_confidence=0.7设定了最小置信度阈值,即规则成立的最小概率为0.7(70%)。
# 算法返回两个结果:itemsets(所有频繁项集的列表)和rules(满足最小支持度和置信度阈值的关联规则列表)。
print(rules)
# 打印出满足上述支持度和置信度阈值的关联规则。
# 关联规则通常表示为"A => B"的形式,其中A是前提条件,B是后件。
# 这些规则有助于揭示数据中的内在关系,例如哪些药材经常一起被开处方。
[{没药} -> {乳香}, {乳香} -> {没药}, {桔梗} -> {甘草}, {川芎, 甘草} -> {当归}, {川芎, 白芍} -> {当归}, {熟地黄, 白芍} -> {当归}, {白芍, 茯苓} -> {当归}, {白术, 茯苓} -> {甘草}, {甘草, 白术} -> {茯苓}, {茯苓, 陈皮} -> {甘草}]
这段代码的目的是使用Apriori算法对中药处方数据进行分析,找出频繁出现的药材组合和强关联规则。通过设置支持度和置信度阈值,算法能够识别出在数据集中频繁共同出现的药材,以及在某些药材出现的情况下其他特定药材出现的概率。这对于理解中医药方的配伍规律和药材使用习惯具有重要意义。
以此方法得到搭配更为频繁的药物组合,最终得到的两种药材的中成药组合如下:
● 桔梗、甘草
● 茯苓、甘草
● 陈皮、甘草
● 川芎、当归
在不提高置信度(0.7),只提高支持度到0.06的情况下,获取药材的有关组合的实现代码如下:
itemsets, rules = apriori(list(df_zhongchengyao["prescription"]), min_support=0.06, min_confidence=0.5)
# 对df_zhongchengyao DataFrame中的"prescription"列数据应用Apriori算法。
# list(df_zhongchengyao["prescription"])将"prescription"列转换为列表形式,作为apriori函数的输入。
# min_support=0.06设置了最小支持度阈值为0.06,这意味着任何被认为是频繁项集的元素组合必须在至少6%的事务中出现。
# min_confidence=0.5设置了最小置信度阈值为0.5,这意味着只有当规则的置信度(即在前件发生后后件发生的条件概率)至少为50%时,才会被认为是有意义的规则。
# 函数返回两个结果:itemsets(频繁项集的集合)和rules(强关联规则的集合)。
print(rules)
# 打印由apriori算法找到的强关联规则。
# 这些规则可以帮助我们理解数据中的特征之间的关联性,例如在中药处方中哪些药材经常一起被使用。
[{川芎} -> {当归}, {桔梗} -> {甘草}, {茯苓} -> {甘草}, {陈皮} -> {甘草}]
这段代码的目的是使用Apriori算法对中药处方数据进行关联规则挖掘,找出满足特定支持度和置信度阈值的药材组合规则。通过设定最小支持度和最小置信度阈值,算法能够识别出在数据集中经常出现的药方组合,以及在某些药材出现时其他特定药材也很可能出现的规则。这些规则对于理解中医药方的配伍规律和药材使用习惯非常有帮助。
得到的两种药材的中成药组合如下::
● 桔梗、甘草
● 没药、乳香(双向)
该数据意味着组合的前项使用更为频繁,或者说药材组合应用更为广泛。
支持度为0.03、置信度为0.7的双药材组合的数据中,{川芎} -> {当归}、{茯苓} -> {甘草}和{陈皮} -> {甘草}全部都在3种药材组合中出现过,而不提高置信度(0.7),只提高支持度到0.06得到的药材组合{桔梗} -> {甘草},{没药} -> {乳香},并没有在3种药材组合中出现过,属于新出现的中药组合。更高的支持度也就意味着更广泛的使用范围,尤其桔梗与甘草的组合在有较高支持度的同时,它们的置信度也在70%以上,也就是在桔梗被频繁应用的同时,它与甘草的共同使用也是常见的。因此,这种组合在深入研究和实际应用中具有较高的价值。另外,没药和乳香之间的相互置信度超过70%,表明该组合使用广泛且经常搭配。
通过上述数据可以得到4种基本的、有效的药物组合:
(1)当归、白芍、甘草。
(2)甘草、当归、茯苓、川芎四、白芍、熟地黄、白术。
(3)桔梗、甘草。
(4)没药、乳香。
四、小结
本文深入探讨了中药材、中成药和中药方剂数据集的应用,通过一系列先进的数据分析技术,为读者揭示了中医药数据挖掘的丰富内涵。以下是对原文的丰富和扩展:
-
数据集的广泛性:本文不仅涵盖了中药材数据,还包括了中成药和中药方剂,为读者提供了一个全面的中医药数据视野。
-
技术的综合运用:文章综合运用了关联规则挖掘算法、分词技术和文本处理技术等多种技术手段,这些技术的结合使用能够更深入地挖掘和分析中医药数据。
-
提取药材别名的重要性:通过文本处理技术,本文特别强调了提取药材别名的重要性,这有助于理解药材的多样性和复杂性,为中药的标准化和国际化奠定基础。
-
中药配方组合的挖掘:利用关联规则挖掘算法,本文成功提取了中药配方中常见的药材组合,这对于理解中医药的配伍原理和临床应用具有重要意义。
-
数据清洗与处理的实践:文章详细介绍了数据清洗和处理的步骤,包括去除噪声、处理缺失值和异常值等,这些步骤对于保证数据质量至关重要。
-
数据挖掘技术的应用:通过实际的数据挖掘案例,本文展示了如何应用数据挖掘技术来发现数据中的有价值信息,如药材的使用频率、药材之间的关联性等。
-
对读者的启发与指导:通过本文的学习,读者不仅能够掌握数据清洗、数据处理和数据挖掘的相关技术,还能够获得将这些技术应用于中医药领域的实践经验。
-
对未来研究的展望:本文还展望了中医药数据挖掘的未来,包括如何利用大数据和人工智能技术进一步推动中医药的现代化和全球化。
通过本文的学习,读者将能够获得宝贵的中医药数据挖掘知识,提升自己在中医药数据分析和研究方面的能力,为中医药的传承与发展做出贡献。
附录
一、代码地址
github:GitHub - XANkui/PythonMachineLearningBeginner: Python 机器学习是利用 Python 编程语言中的各种工具和库来实现机器学习算法和技术的过程。Python 是一种功能强大且易于学习和使用的编程语言,因此成为了机器学习领域的首选语言之一。这里我们一起开始一场Python 机器学习基础入门到精通学习旅程。
二、文中的关联图(带权有向图)简单绘制代码参考
1、安装 networkx 库
!pip install networkx
# 这行代码是一个命令,用于在支持shell命令的环境中(如Jupyter Notebook)执行pip安装操作。
# pip是Python的包管理工具,用于安装Python库。
# 该命令尝试安装networkx库,这是一个Python库,用于创建、操作复杂网络的结构、绘制和分析。
# NetworkX提供了丰富的数据结构和方法,用于处理图论中的对象,例如节点(nodes)、边(edges)和路径(paths)。
# 如果networkx库尚未安装在当前Python环境中,执行此命令将从Python包索引(PyPI)下载并安装它及其依赖项。
# 如果networkx已经安装,此命令将检查是否有可用的更新。
这条命令通常在数据分析、复杂网络分析、图论研究等领域使用,确保所需的库已经安装在Python环境中,以便可以执行相关的图计算和可视化任务。如果库已经安装且是最新版本,则此命令可能不会有任何效果;如果有更新,则会进行升级。
2、绘制
import matplotlib.pyplot as plt
import networkx as nx
import matplotlib.font_manager as fm
# 导入所需的库。matplotlib.pyplot用于绘图,networkx用于创建和操作图结构,matplotlib.font_manager用于字体管理。
# 设置中文字体路径
font_path = 'C:/Windows/Fonts/simhei.ttf' # 指定中文字体路径,以便绘制中文标签
# 你可以根据自己的系统字体选择合适的中文字体文件路径。
# 设置字体属性
zh_font = fm.FontProperties(fname=font_path)
# 创建一个FontProperties对象,用于指定绘制中文时使用的字体。
# 创建一个有向图
G = nx.DiGraph()
# 使用networkx的DiGraph类创建一个有向图对象。
# 添加带权边
edges = [
# 定义图的边和权重的列表,例如("陈皮", "炙苓", 1)表示从陈皮到炙苓的边,权重为1。
("陈皮", "炙苓", 1),
("炙苓", "甘草", 1),
("白术", "炙苓", 1),
("川芎", "炙苓", 1),
("川芎", "白芍", 1),
("白芍", "甘草", 1),
("白芍", "熟地黄", 1),
("熟地黄", "当归", 1),
("当归", "白芍", 4),
("甘草", "当归", 3),
("甘草", "白芍", 1),
("甘草", "川芎", 1),
("甘草", "炙苓", 1),
("白芍", "甘草", 2)
]
for edge in edges:
G.add_edge(edge[0], edge[1], weight=edge[2])
# 遍历edges列表,使用add_edge方法向图中添加带权重的边。
# 设置节点颜色
color_map = {
# 定义一个字典,指定特定节点的颜色。
"炙苓": 'lightyellow',
"甘草": 'lightcoral',
"当归": 'plum',
"白芍": 'lightcoral',
}
node_colors = [color_map.get(node, 'lightgrey') for node in G.nodes]
# 为图中的每个节点指定颜色,如果节点在color_map中有对应颜色,则使用该颜色,否则使用默认的灰色。
# 绘制图
pos = nx.spring_layout(G)
# 使用spring_layout布局算法为图中的节点生成位置。
nx.draw(G, pos, with_labels=False, node_color=node_colors, node_size=2000, edge_color='black')
# 使用networkx的draw函数绘制图,设置节点颜色和大小,以及边的颜色。
# 绘制节点标签
for node, (x, y) in pos.items():
plt.text(x, y, node, fontsize=10, fontweight='bold', fontproperties=zh_font, ha='center', va='center')
# 在每个节点的位置绘制节点名称,使用指定的中文字体。
# 添加权重标签
edge_labels = nx.get_edge_attributes(G, 'weight')
# 获取图中所有边的权重。
# 绘制边标签
for (n1, n2), label in edge_labels.items():
x1, y1 = pos[n1]
x2, y2 = pos[n2]
plt.text((x1 + x2) / 2, (y1 + y2) / 2, label, fontsize=10, fontproperties=zh_font, color='black', ha='center')
# 在每条边的中点位置绘制权重标签,使用指定的中文字体。
plt.savefig('Images/01Main-01.png', bbox_inches='tight')
# 将绘制的图保存为图片文件,bbox_inches='tight'确保所有内容都被包含在内,没有被裁剪。
# 显示图
plt.show()
# 显示绘制的图。
这段代码的目的是使用NetworkX和Matplotlib库创建和绘制一个有向图,图中的节点代表中药材,边代表药材之间的关联关系,边上的权重表示关联的强度。代码中还设置了中文字体以确保中文标签可以正确显示,并且定义了节点颜色和边的权重标签。最后,将绘制的图保存为图片文件,并显示出来。