Jean's Blog

一个专注软件测试开发技术的个人博客

0%

RAG组件--数据导入之csv解析

CSVLoader自动切分数据

  • 核心功能:CSVLoader能自动将CSV文件切分为多个文档,每行数据生成一个独立文档
  • 元数据结构:每个文档包含page_content和metadata两部分,metadata记录数据来源和行号

基本加载 CSV 文件并打印记录

  • 实现方式:使用加载数据
  • 自动解析:能智能识别CSV中的字段名(如Name、Description、PowerLevel)
  • 输出结构:
    • page_content包含分类、名称、描述和等级信息
    • metadata包含源文件路径和行号信息

示例代码

1
2
3
4
5
6
7
8
9
10
from langchain_community.document_loaders import CSVLoader

file_path = "../data/黑悟空/黑神话悟空.csv"

loader = CSVLoader(file_path=file_path)
data = loader.load()
print("示例 1: 基本加载 CSV 文件并打印前两条记录")
for record in data[:2]:
print(record)
print("-" * 80)

执行结果,输出内容:

1
2
3
4
5
6
7
8
9
10
示例 1: 基本加载 CSV 文件并打印前两条记录
page_content='Category: 装备
Name: 铜云棒
Description: 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。
PowerLevel: 85' metadata={'source': '../data/黑悟空/黑神话悟空.csv', 'row': 0}
page_content='Category: 装备
Name: 百戏衬钱衣
Description: 一件精美的战斗铠甲,能够提供强大的防御并抵御剧毒伤害。
PowerLevel: 90' metadata={'source': '../data/黑悟空/黑神话悟空.csv', 'row': 1}
--------------------------------------------------------------------------------

跳过 CSV 文件的标题行并使用自定义列名

  • 参数配置:通过csv_args设置delimiter、quotechar和fieldnames

  • 字段映射:可将原始字段映射为中文名称(如”种类”、”名称”、”说明”、”等级”)

  • 注意事项:若保留标题行,第一行数据会被解析为字段定义行

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from langchain_community.document_loaders import CSVLoader

file_path = "../data/黑悟空/黑神话悟空.csv"

loader = CSVLoader(
file_path=file_path,
csv_args={
"delimiter": ",",
"quotechar": '"',
"fieldnames": ["种类", "名称", "说明", "等级"],
},
)
data = loader.load()

print("示例 2: 跳过标题行并使用自定义列名")
for record in data[:2]:
print(record)
print("-" * 80)

执行结果,输出内容

1
2
3
4
5
6
7
8
9
10
示例 2: 跳过标题行并使用自定义列名
page_content='种类: Category
名称: Name
说明: Description
等级: PowerLevel' metadata={'source': '../data/黑悟空/黑神话悟空.csv', 'row': 0}
page_content='种类: 装备
名称: 铜云棒
说明: 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。
等级: 85' metadata={'source': '../data/黑悟空/黑神话悟空.csv', 'row': 1}
--------------------------------------------------------------------------------

指定 “Name” 列作为 source_column

  • 功能作用:将指定列的值作为metadata中的source字段

  • 应用场景:便于后续基于名称字段进行精确检索和过滤

  • 实现效果:metadata中的source从文件路径变为该行Name列的具体值

1
2
3
4
5
6
7
8
9
10
11
from langchain_community.document_loaders import CSVLoader

file_path = "../data/黑悟空/黑神话悟空.csv"

loader = CSVLoader(file_path=file_path, source_column="Name")
data = loader.load()

print("示例 3: 使用 'Name' 列作为主要内容来源")
for record in data[:2]:
print(record)
print("-" * 80)

执行结果,输出内容

1
2
3
4
5
6
7
8
9
10
示例 3: 使用 'Name' 列作为主要内容来源
page_content='Category: 装备
Name: 铜云棒
Description: 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。
PowerLevel: 85' metadata={'source': '铜云棒', 'row': 0}
page_content='Category: 装备
Name: 百戏衬钱衣
Description: 一件精美的战斗铠甲,能够提供强大的防御并抵御剧毒伤害。
PowerLevel: 90' metadata={'source': '百戏衬钱衣', 'row': 1}
--------------------------------------------------------------------------------

使用 UnstructuredCSVLoader 加载 CSV 文件

  • CSVLoader特点:
    • 按行切分数据,每行生成独立文档
    • 保留详细元数据(行号、源文件等)
    • 支持字段映射和源列指定
  • UnstructuredCSVLoader特点:
    • 将整个CSV文件作为单个文档处理
    • 保持原始表格格式
    • 适合需要保留表格整体结构的场景
  • 选择建议:根据是否需要行级细粒度控制选择合适loader

示例代码

1
2
3
4
5
6
7
8
9
10
11
from langchain_community.document_loaders import CSVLoader

file_path = "../data/黑悟空/黑神话悟空.csv"

from langchain_community.document_loaders import UnstructuredCSVLoader

loader = UnstructuredCSVLoader(file_path=file_path)
data = loader.load()
print("示例 4: 使用 UnstructuredCSVLoader 加载文件")
print(data)
print("-" * 80)

执行结果,输出内容

1
2
3
4
示例 4: 使用 UnstructuredCSVLoader 加载文件
[Document(metadata={'source': '../data/黑悟空/黑神话悟空.csv'}, page_content='Category Name Description PowerLevel 装备 铜云棒 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。 85 装备 百戏衬钱衣 一件精美的战斗铠甲,能够提供强大的防御并抵御剧毒伤害。 90 技能 天雷击 召唤天雷攻击敌人,造成大范围雷电伤害。 95 技能 火焰舞 施展火焰舞步,将敌人包围在炽热的火焰之中。 92 人物 悟空 主角,拥有七十二变和腾云驾雾的能力,行侠仗义。 100 人物 银角大王 强大的妖王之一,擅长操控各种法宝,具有极高的战斗力。 88')]
--------------------------------------------------------------------------------

CSVLoader与 UnstructuredCSVLoader的比较

DirectoryLoader与CSVLoader结合使用

1
2
3
4
5
6
7
8
9
10
11
12
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.document_loaders import CSVLoader

loader = DirectoryLoader(
path="../data/黑悟空", # Specify the directory containing your CSV files
glob="**/*.csv", # Use a glob pattern to match CSV files
loader_cls=CSVLoader # Specify CSVLoader as the loader class
)

docs = loader.load()
print(f"文档数:{len(docs)}") # 输出文档总数
print(docs[0])

执行结果,输出内容

1
2
3
4
5
文档数:6
page_content='Category: 装备
Name: 铜云棒
Description: 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。
PowerLevel: 85' metadata={'source': '../data/黑悟空/黑神话悟空.csv', 'row': 0}

只使用DirectoryLoader(结构化保持的重要性)

  • 问题现象:使用通用加载器时,CSV的列数据(如Category/Name/Description/PowerLevel)会被合并为单个字符串字段
  • 关键缺陷:
    • 字段混淆:所有列值混入page_content字段,失去原始表格结构
    • 查询困难:无法直接查询特定字段(如查询PowerLevel=100的装备)
    • 元数据单一:metadata仅保留文件路径,不包含列级元信息
1
2
3
4
5
6
7
8
9
10
11
from langchain_community.document_loaders import DirectoryLoader

loader = DirectoryLoader(
path="../data/黑悟空", # Specify the directory containing your CSV files
glob="**/*.csv", # Use a glob pattern to match CSV files
# loader_cls=CSVLoader # Specify CSVLoader as the loader class
)

docs = loader.load()
print(f"文档数:{len(docs)}") # 输出文档总数
print(docs[0])

执行结果,输出内容:

1
2
文档数:1
page_content='Category Name Description PowerLevel 装备 铜云棒 一根结实的青铜棒,挥舞时能发出破空之声,适合近战攻击。 85 装备 百戏衬钱衣 一件精美的战斗铠甲,能够提供强大的防御并抵御剧毒伤害。 90 技能 天雷击 召唤天雷攻击敌人,造成大范围雷电伤害。 95 技能 火焰舞 施展火焰舞步,将敌人包围在炽热的火焰之中。 92 人物 悟空 主角,拥有七十二变和腾云驾雾的能力,行侠仗义。 100 人物 银角大王 强大的妖王之一,擅长操控各种法宝,具有极高的战斗力。 88' metadata={'source': '../data/黑悟空/黑神话悟空.csv'}

UnstructuredCSVLoader的局限性

  • 核心问题:
    • 数据扁平化:将CSV表格转换为纯文本字符串,示例中的装备属性(铜云棒/百戏衬钱衣)与数值(85/90)失去关联性
    • 解析障碍:需要手动拆分字符串才能获取特定字段值,增加后续处理复杂度

加载器选择策略

  • 优选方案:
    • CSVLoader:自动解析列结构,保留字段对应关系(如”百戏衬钱衣”与其防御值90保持绑定)
    • 指定加载场景:在DirectoryLoader中强制使用loader_cls=CSVLoader参数
  • 避坑原则:
    • 避免通用解析:不依赖UnstructuredCSVLoader的自动解析功能
    • 显式声明结构:对表格类数据必须指定专用加载器
  • 实战示例:黑神话悟空CSV中的装备数据(铜云棒85攻击力/百戏衬钱衣90防御力)需要完整保留数值关联