以思维的速度用英语编码
在最近的一项研究中,我向ChatGPT提出了一个挑战,要求它在赢得我的忠诚度方面超越Kroger的营销部门。当将我的交易历史输入到生成式AI中时,它能否创建出比每周优惠券更具吸引力的营销策略,例如针对鸡蛋和农产品?
更广泛的问题是,ChatGPT能否为营销人员提供有价值的客户洞察和消费者营销策略,以实现增长和留存,使用真实世界的数据进行大规模个性化。该实验将使用我的购物收据来测试ChatGPT在有限数据集上进行业务分析的能力。
但是,我在一开始就遇到了一个常见的数据分析挑战:
-
这些收据以非结构化的Adobe PDF文档的形式存储。正确格式化数据是第一个关键步骤。
-
数据仅限于产品描述、SKU和定价。需要进行数据丰富以进行聚合分析、可视化和洞察。
本文涵盖以下内容:
-
挑战:将PDF收据转换为可操作数据
-
使用ChatGPT进行OCR和ETL
-
使用ChatGPT将英语翻译为Python代码
-
从数据中提取洞察和可视化
使用ChatGPT,无需编码经验
通常,数据集成工具或开发环境对于这些ETL(提取、转换、加载)任务非常有用,但这需要时间、资源、软件和编码技能,而对于十多年没有接触过编码键盘的人来说,这些都是稀缺资源。
我需要ChatGPT来接收PDF文件,使用OCR(光学字符识别)从页面中提取文本,然后识别模式将文本转换为数据,全部使用英语,而不是代码。
接下来是使用ChatGPT作为编码伙伴的方法,它会自动将对话提示转换为Python代码。能够生成和执行完全功能的逻辑,并以如此快的速度完成一系列复杂任务,让我感到震惊。我与ChatGPT共同创建(我使用这个词很宽泛)了完全功能的Python代码,尽管我对Python一无所知。
"作为一名业务分析师和主题专家,我从未开发过Python,我不知道ChatGPT创建的代码是否高效。我不知道它是否漂亮。我甚至不知道代码是否可扩展。但作为一名业务分析师,如果代码能够帮助我完成任务,我不在乎。"
本文中分享了一些代码示例,以展示自动化的速度,但您永远不需要编写一行代码。传统的开发环境和专门的资源都是不必要的。对于您的项目,您将使用英语编码,ChatGPT作为您的合作者,将指令翻译为所需的业务结果。
ChatGPT创建OCR逻辑:从PDF到文本
第一步是上传一个样本PDF,并告诉ChatGPT目标是“推断洞察力”。ChatGPT无法直接提取数据,但建议下一步使用基于图像的OCR。
ChatGPT进一步建议使用pytesseract
库,它是Google的Tesseract-OCR引擎的Python绑定,以及pdf2image
将PDF页面转换为图像。然后使用OCR将字符图像转换为文本。
我以前从未听说过这个功能,它似乎像无限宝石一样强大,但我有什么理由反驳呢?
ChatGPT生成的初始逻辑证实了OCR正在工作。字符被识别出来,但还没有以连贯的方式。幸运的是,数据似乎是以列格式呈现的,为数据转换提供了良好的基础。
# 成功的OCR例子输出
第1页:
wr ro jer
{et Order Type: In Store
Order Date: October 21, 2023
Original Item Total
Item Coupons/Sales
Order Coupons
Sales Tax
Order Total
Item Details
0780467311001
1x $0.79 each
UPC: 0780467311001
Birds Eye® Veggie Made Frozen Original Mashed Cauliflower, 12 oz
2 x $3.49 $4-29 each
...
下一步,也是ChatGPT建议的,是将该文本转换为一系列可由下游解析程序迭代处理的行。在尝试处理所有页面后导致超时失败后,这种逐行处理的方法成为推荐策略。这似乎是合理的;我假设每个用户会话在沙盒环境中可能有自己的内存限制。
# ChatGPT自动生成的代码,用于从OCR创建数据行
import re
import pandas as pd
from PIL import Image
import pytesseract
from pdf2image import convert_from_path
# 使用Pytesseract执行OCR的函数
def ocr_pdf_to_text_with_page_number_v2(pdf_path):
ocr_output = []
images = convert_from_path(pdf_path)
for page_num, image in enumerate(images):
text = pytesseract.image_to_string(image)
lines = text.split('\n')
for line in lines:
ocr_output.append({"text": line, "page": page_num + 1})
return ocr_output
以下是它的工作原理:
-
导入库:
pdf2image
库将PDF文件转换为图像列表。PIL(Pillow)用于打开、处理和保存图像文件。 -
将PDF转换为图像: 函数
convert_from_path
从pdf2image
中获取上传到本次会话或后续会话的PDF文件的路径,并返回一个图像列表,每个图像对应于PDF中的一页。 -
OCR循环: 然后,该函数使用
image_to_string()
对每个图像进行迭代,执行OCR以提取文本。然后,将每个页面的文本添加到ocr_text
中,并在前面加上页码。这在调试时非常有用,我可以要求ChatGPT输出“第2页的前10行”以进行验证。 -
返回OCR文本: 最后,返回在
ocr_text
中收集到的完整文本。
# 处理后的OCR字符串中的数据示例输出
[
{'text': 'wr ro jer', 'page': 1},
{'text': '', 'page': 1},
{'text': '{et Order Type: In Store', 'page': 1},
{'text': 'Order Date: October 21, 2023', 'page': 1},
{'text': '', 'page': 1}
...
...
{'text': '1x $1.99 $2-49 each', 'page': 2},
{'text': 'Item Coupon/Sale: -$0.50', 'page': 2},
{'text': 'UPC: 0001111058711', 'page': 2},
...
]
引导ChatGPT解析文档
映射文档模式不仅是技术性的练习;它是数据转换的关键步骤。仅仅指示ChatGPT“找到产品”是不会成功的。它需要人眼来指导逻辑流程,以进行数据转换。
在仔细检查收据和一些样本OCR输出后,我有了一个很好的起点,可以开始指导ChatGPT提取和转换文本为数据集,使用英语和伪代码。
数据结构相当良好,如下面的映射示例所示。每个产品序列以描述开始,后面总是跟着数量和单价。最后一行总是UPC编号。
唯一棘手的部分是折扣和优惠券的可变性。对于以全价销售的产品,将找不到“Item Coupon/Sale”行,但对于有折扣的产品,会找到。更具挑战性的是多单位销售的呈现方式。如果购买了三包冷冻豌豆,并附带优惠券或折扣,将有一行“Quantity = 3”,但是3个单独的“Item Coupon”行。
上述映射被转换为一个英文文本提示,经过几次调试。这种交互感觉像是与一个编码同事交谈。将来,我可能会尝试使用语音转文本来消除我的手指作为中间人,以实现更快的配对开发。
# 向ChatGPT解释用于OCR到数据转换的逻辑
逐行解析逻辑,并将行和字段保存到数据表中。
1. 跳过空行
2. 元数据位于行“Item Details”上方
3. 详细交易数据位于行“Item Details”下方和行“Order Coupons”上方
4. 从元数据中,找到以文本“Order Date:”开头的行。冒号后面的文本是数据表的“Order Date”。将月份、日期和年份的格式转换为日期格式
5. 行“Item Details”标志着我们产品详细信息集的开始。
集合中有1-n个产品,它们遵循以下模式:
产品描述行:[产品描述] <-- 必需(一次)
数量价格行:数量 "x" [$单价] [$原价(可选)] " each"
折扣行:以“Item Coupon”和“$”开头的可选行
UPC行:“UPC:” + [UPC] <-- 必需
这是一个产品详细信息行序列的详细模式:
数据转换代码如下:
# FINAL RECEIPT PARSING CODE AUTOMATICALLY CREATED BY CHATGPT
def adjusted_parse_ocr_text_with_page_number_v2(ocr_data, file_name):
data_table = []
debug_output = []
current_state = "metadata"
order_date = None
product_description_candidate = None
quantity = None
unit_price = None
is_discounted = "No"
upc = None
current_page = 1
quantity_unit_price_pattern = re.compile(r"(\d+)[\s|x]*\$([\d.]+)")
def reset_product_variables():
nonlocal product_description_candidate, quantity, unit_price, is_discounted, upc
product_description_candidate = None
quantity = None
unit_price = None
is_discounted = "No"
upc = None
for line in ocr_data:
text = line["text"].strip()
page_num = line["page"]
if not text or text.startswith("$"):
continue
if page_num == 1:
if current_state == "metadata":
if "Order Date:" in text:
date_str = text.split(":", 1)[1].strip()
try:
order_date = datetime.strptime(date_str, "%B %d, %Y").date()
except ValueError:
order_date = "Error in date format"
if "Item Details" in text:
current_state = "product"
reset_product_variables()
continue
if page_num > current_page:
current_page = page_num
current_state = "product"
reset_product_variables()
if current_state == "product":
if "Order Coupons" in text:
break
elif not product_description_candidate:
product_description_candidate = text.strip()
elif not quantity:
match = quantity_unit_price_pattern.search(text)
if match:
product_description = product_description_candidate
quantity = int(match.group(1))
unit_price = float(match.group(2))
else:
product_description_candidate = text.strip()
continue
elif "Item Coupon" in text:
is_discounted = "Yes"
elif "UPC:" in text:
upc = text.split(":", 1)[1].strip()
data_row = {
"File Name": file_name,
"Order Date": order_date,
"Product Description": product_description,
"UPC": upc,
"Quantity": quantity,
"Is Discounted": is_discounted,
"Unit Price": unit_price
}
data_table.append(data_row)
reset_product_variables()
return data_table
这段代码是用于解析OCR文本并将其转换为结构化数据表的函数。它会遍历OCR数据的每一行,根据特定的规则提取所需的信息,并将其存储在数据表中。
要使用这段代码,您需要将OCR数据作为输入,并提供文件名。函数将返回一个包含解析后数据的数据表。
您可以使用以下代码示例来调用这个函数:
# 调用解析函数
parsed_data = adjusted_parse_ocr_text_with_page_number_v2(ocr_data, file_name)
# 将解析后的数据保存为CSV文件
df = pd.DataFrame(parsed_data)
df.to_csv("parsed_data.csv", index=False)
请注意,您需要将OCR数据和文件名作为参数传递给函数,并将解析后的数据保存为CSV文件。被要求根据产品描述中的关键词尝试对“类别”维度进行划分,ChatGPT生成了一个用于文本分析的初步列表。
# 定义一个根据关键词对产品进行分类的函数
def categorize_product(product_description):
product_description = product_description.lower()
# 定义类别和关键词
categories = {
'杂货': ['面包', '谷类食品', '意大利面', '大米'],
'乳制品': ['牛奶', '奶酪', '酸奶', '黄油', '鸡蛋'],
'肉类': ['牛肉', '鸡肉', '猪肉', '鱼'],
'蔬菜': ['胡萝卜', '花椰菜', '西兰花', '玉米'],
'水果': ['苹果', '香蕉', '橙子', '浆果'],
'零食': ['薯片', '饼干', '曲奇饼', '爆米花'],
'饮料': ['果汁', '苏打水', '水', '咖啡', '茶'],
'冷冻食品': ['冰淇淋', '冷冻'],
'杂项': []
}
# 对产品进行分类
for category, keywords in categories.items():
for keyword in keywords:
if keyword in product_description:
return category
return '杂项'
# 将函数应用于数据框
combined_df['产品类别'] = combined_df['产品描述'].apply(categorize_product)
# 显示更新后的数据框的前几行
combined_df.head()
通过人工修正数据
由于固定宽度和屏幕换行的挑战,完整的工作文件被下载为CSV文件,以便更容易进行可视化审查。对产品类别的第一次尝试接近了,但并不完美。即使具有先进的AI能力,也有时候人类的直觉和专业知识是无法替代的。ChatGPT需要额外的上下文线索来更新产品类别。
这也感觉像是一次对数据丰富的初级开发人员进行指导的对话。这个练习就像是简单地提示“如果你看到‘蛋糕’这个词,那就是‘糕点’”。
ChatGPT不仅可以理解“如果这样,那么那样”的语法,我还可以简化提示,就像我在和同事交谈一样。ChatGPT知道当我说“更新那个”时,我指的是“产品类别”。这是一个改变游戏规则的举措。我仍然可以用简单的英语来转换数据!
经过几次迭代来清理产品类别字段后,我们准备进行聚合数据分析。
使用ChatGPT可视化数据
高级数据分析不仅允许创建和执行代码,而且在同一个会话中,我们现在可以可视化数据,以便使复杂的数据变得易于理解和可操作。
在这里,指令可以用英语提示,“请绘制一个按周升序排列的堆叠条形图,从最早的周开始,显示每个类别的数量乘以单价的总和。因此,第1周将显示面包、饮料等的总金额。第二个图应该是一个饼图,显示按类别分组的所有周的总和,并显示百分比。”
片刻之后,ChatGPT分享了一些经过精细调整的图表,以供进一步分析。
从数据中获取洞察
图表和图形非常有趣,但需要进行视觉检查才能发现洞察。我们可以利用ChatGPT来推断数据中的模式,并提供独特的观点。最终,这个ETL练习的目标是生成洞察、分析或与其他数据源集成。
通过最少的指导,我们可以要求ChatGPT对数据进行询问,并提供其发现的洞察的观点。
无障碍的业务分析
这样开始了一段关于业务分析的旅程。这个练习的目标不是开发用于字符识别或使用OCR进行PDF转换的Python代码。那只是通往分析数据集以获取新的洞察和方向的必要步骤。
将存储在Adobe PDF中的非结构化数据转化为可理解和可操作的数据是阻碍我完成真正任务的第一个障碍——创建购物者人物和制定营销策略。
作为一个在职业生涯的早期阶段从事数据集成、转换和数据分析的人员,使用英语(或您的母语)以思维的速度来开发转换逻辑是非常有趣的。