
最近在管理项目做QA时,接到一个对 LSPs(Language Service Providers)和我们自己团队的LMs(Language Managers)进行 LQA 时进行工作质量评估的需求。需求内容包括对以上双方在进行review时的修改力度、错误类型、质量保证等内容进行全方位的评估,从而找出双方在进行质量管理以及翻译的工作风格上的差别。考虑到需求覆盖 20 多种语言,涉及 15,000+ 个项目,并且在这一过程中,我需要批量对每个项目及同一项目不同版本的 LQA 得分和 LM 给出的错误类型进行统计,数据量庞大,人工逐一提取和统计显然不切实际。因此,工作向“自动化”方向的转变不可避免。在看了几个从 CAT 工具导出后的 LQA 报告后,发现它们的格式其实都具有一定的共性。继而考虑是否可以用 Python 实现批量数据提取和信息汇总。通过探索和实践,找到了一些高效的解决方案,希望在此与大家分享。这篇博客将介绍多种方法对Excel 表中特定单元格计算结果进行批量提取和汇总,涉及到Pandas, openpyxl, xlwings,PyExcelerate 等库。同时,我们还可以稍微拓展下后续数据分析能用到的与数据可视化相关的库,如:matplotlib, Plotly等。
需求分析
因为我们想要探索的这些方法并非只针对某种特定格式的xlsx文件,因此在解决问题之前,我们先对接到的需求进行分析,对操作中可能遇到的各种问题进行多个合理假设,从而最终得出一些共性的解决方案。
- 对于每个 Excel 文件,我们需要提取多个工作表中的数据,并且这些工作表的名称可能是动态的,而不是像我们熟悉的仅针对 文件的第一个sheet进行操作。我们需要遍历所有工作表,找出名称中包含特定关键字(如 "LQA_Data")的工作表进行数据提取。
- 提取的数据范围也许会更为复杂,可能分布在不同的不连续区域,例如,我们需要从 A1:A10 区域提取一些基础信息,从 B20:D30 区域提取一些详细数据,同时还要从以 "Summary_" 开头的列(如 "Summary_Review"、"Summary_Errors" 等)所在的行中提取汇总信息。这些数据包含不同的数据类型,包括文本、数字和日期,需要对其进行针对性的处理。
- 对于提取所得到的数据,我们无法假设他们中的所有内容都是我们想要的,因此要进行适当的数据清洗及转换操作。对于文本数据,除了基本的清理(去除多余空格和特殊字符)外,也许还需要进行文本的分词处理,将长文本拆分成多个关键词,以便后续的文本分析;对于数字数据,需要根据不同的统计需求进行计算,例如计算平均值、标准差等;对于日期数据,要将其转换为统一的日期格式,并计算日期之间的时间差。
- 考虑到数据分析以及后续报告的写作,在提取和处理数据后,如果能根据这些数据直接生成可视化的报告那就再好不过了,可视化的内容包括但不限于柱状图、折线图和饼图,以直观展示不同语言、不同项目、不同 user 之间的质量指标对比,如不同语言的错误率分布、不同项目的修改力度变化、同一项目不同 LM 的修改力度变化等。
- 为便于与其他系统或服务进行数据交换和集成,除了将数据存储到数据库以及写入新的 Excel 文件外,我们还要将数据存储为 JSON 格式,
方案一:使用 openpyxl 进行复杂数据处理和存储
(一)实现思路:
首先,遍历桌面目录下的所有.xlsx 文件,使用 openpyxl 打开每个文件并加载其工作表。
对于不同的区域(如 A1:A10、E15:G20、C11:F11 等),分别进行数据提取。对于文本数据,使用 Python 的字符串处理方法进行清洗;对于数字数据,进行异常值检查和处理。
对于筛选特定行的数据,根据相应的条件判断行是否满足要求,满足则提取相应的数据。
将提取的数据存储在不同的数据结构中,如字典或列表中,并对其进行整理和转换,以便存储到数据库中。
使用数据库连接库(如 sqlite3 或 psycopg2 等)将数据存储到数据库的相应表中。
同时,将数据存储到一个新的 Excel 文件中,使用 openpyxl 进行文件创建和数据写入。
(二)代码展示:
import os
import pandas as pd
from openpyxl import load_workbook
import sqlite3
import re
def clean_text(text):
# 去除多余空格和特殊字符
cleaned_text = re.sub(r'\s+', ' ', text).strip()
# 这里可以添加更多的文本清洗逻辑,例如将缩写转换为完整词汇
return cleaned_text
def check_numeric(value):
if isinstance(value, (int, float)):
if value < 0 or value > 1000000: # 假设极大值为 1000000,可根据实际情况调整
return None # 标记异常值
return value
def store_data_in_db(data, conn):
cursor = conn.cursor()
# 假设我们有一个名为 lqa_data 的表,表结构根据实际情况设计
cursor.execute('CREATE TABLE IF NOT EXISTS lqa_data (project_id TEXT, start_date TEXT, error_type TEXT, translation_accuracy REAL, word_count INTEGER, error_rate REAL)')
for item in data:
cursor.execute('INSERT INTO lqa_data (project_id, start_date, error_type, translation_accuracy, word_count, error_rate) VALUES (?,?,?,?,?,?)',
(item['project_id'], item['start_date'], item['error_type'], item['translation_accuracy'], item['word_count'], item['error_rate']))
conn.commit()
# 获取桌面路径
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
# 初始化数据库连接
conn = sqlite3.connect('lqa_data.db')
# 初始化结果列表
result = []
# 遍历桌面目录下的所有.xlsx文件
for filename in os.listdir(desktop_path):
if filename.endswith('.xlsx'):
file_path = os.path.join(desktop_path, filename)
try:
wb = load_workbook(file_path)
sheet = wb.active
# 提取 A1:A10 区域的文本数据并清洗
project_info = [clean_text(sheet[f'A{i}'].value) for i in range(1, 11)]
project_id = project_info[0] # 假设第一个单元格是项目编号
start_date = project_info[1] # 假设第二个单元格是项目开始日期
# 提取 E15:G20 区域的数据并检查数值有效性
error_data = []
for i in range(15, 21):
row_data = []
for col in ['E', 'F', 'G']:
value = sheet[f'{col}{i}'].value
checked_value = check_numeric(value)
row_data.append(checked_value)
error_data.append(row_data)
# 提取 C11:F11 区域的数据
translation_metrics = [sheet[f'C11'].value, sheet[f'D11'].value, sheet[f'E11'].value, sheet[f'F11'].value]
translation_accuracy = translation_metrics[0] # 假设 C11 是翻译准确率
# 提取 F7 单元格的内容作为 Word Count
word_count = sheet['F7'].value
# 假设错误数量存储在 G7 单元格
error_count = sheet['G7'].value
error_rate = error_count / word_count if word_count else 0 # 计算错误率
# 将提取的数据放入一个字典
data_item = {
'project_id': project_id,
'start_date': start_date,
'error_type': str(error_data), # 可根据实际情况存储更详细的错误类型信息
'translation_accuracy': translation_accuracy,
'word_count': word_count,
'error_rate': error_rate
}
result.append(data_item)
# 将数据存储到数据库
store_data_in_db([data_item], conn)
except Exception as e:
print(f"Error processing file {filename} with openpyxl: {e}")
# 将部分数据存储到一个新的 Excel 文件中,这里仅作为示例存储部分数据
result_df = pd.DataFrame(result, columns=["project_id", "start_date", "error_type", "translation_accuracy", "word_count", "error_rate"])
result_df.to_excel(os.path.join(desktop_path, 'extracted_data_openpyxl_complex.xlsx'), index=False)
print("批量提取、处理和存储完成,结果已保存为 'extracted_data_openpyxl_complex.xlsx' 和存储在数据库中")
(三)部分代码解释:
clean_text 函数:使用正则表达式去除文本中的多余空格和特殊字符,并可根据需要添加更多的文本清洗逻辑。
check_numeric 函数:检查数字数据是否存在异常值(这里假设小于 0 或大于 1000000 为异常),对于异常值进行标记(返回 None)。
store_data_in_db 函数:将数据存储到 SQLite 数据库的 lqa_data 表中,使用 sqlite3 库进行数据库操作。
在遍历文件时,分别对不同区域的数据进行提取和处理:
对于 A1:A10 区域的数据,进行文本清洗。
对于 E15:G20 区域的数据,进行数字有效性检查。
对于 C11:F11 区域的数据,提取翻译指标。
计算错误率,根据 F7 的 “Word Count” 和 G7 的错误数量计算。
将处理后的数据存储在字典中,并存储到数据库和新的 Excel 文件中。
方案二:使用 xlwings 进行高级数据交互和存储
(一)实现思路:
利用 xlwings 启动 Excel 应用程序,并隐藏界面以提高效率。
遍历每个 Excel 文件,打开文件的多个工作表(如果存在),对于每个工作表,根据不同的需求提取数据。
利用 xlwings 的强大功能,直接调用 Excel 的函数对数据进行处理,例如使用 Excel 的 VLOOKUP 函数查找相关数据,使用 SUM 函数对某些数据进行汇总。
对于筛选特定行的数据,使用 xlwings 调用 Excel 的筛选功能进行筛选操作。
将提取的数据存储在不同的数据结构中,并使用 pandas 将数据整理成 DataFrame。
利用 sqlalchemy 库将数据存储到不同类型的数据库(如 PostgreSQL)中,确保数据存储符合数据库的高级特性,如事务处理、外键关联等。
同时,将数据存储到一个新的 Excel 文件中,使用 xlwings 确保文件的格式和样式符合需求,例如添加表头样式、数据行的颜色标记等。
(二)代码展示:
import os
import pandas as pd
import xlwings as xw
from sqlalchemy import create_engine
def store_data_in_db(data, engine):
df = pd.DataFrame(data)
# 假设表名为 lqa_data,根据实际情况调整表结构和存储逻辑
df.to_sql('lqa_data', engine, if_exists='append', index=False)
# 获取桌面路径
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
# 初始化数据库引擎,这里使用 PostgreSQL 作为示例
engine = create_engine('postgresql://username:password@localhost:5432/lqa_db')
# 初始化结果列表
result = []
# 使用xlwings启动Excel应用(用于获取公式计算结果)
with xw.App(visible=False) as app:
# 遍历桌面目录下的所有.xlsx文件
for filename in os.listdir(desktop_path):
if filename.endswith('.xlsx'):
file_path = os.path.join(desktop_path, filename)
try:
wb = app.books.open(file_path)
# 假设第一个工作表是我们需要处理的工作表
sheet = wb.sheets[0]
# 提取 A1:A10 区域的数据,使用 Excel 的文本函数进行清洗
project_info = sheet.range('A1:A10').formula_array = "=TRIM(SUBSTITUTE(A1:A10, CHAR(10), ' '))"
project_id = sheet.range('A1').value
start_date = sheet.range('A2').value
# 提取 E15:G20 区域的数据,使用 Excel 的函数检查数值有效性
error_data = sheet.range('E15:G20').formula_array = "=IF(E15:G20<0, NULL, E15:G20)"
# 可以使用 xlwings 的其他功能来获取处理后的数据
# 提取 C11:F11 区域的计算结果
translation_metrics = sheet.range('C11:F11').value
translation_accuracy = translation_metrics[0]
# 提取 F7 单元格的内容作为 Word Count
word_count = sheet.range('F7').value
# 假设错误数量存储在 G7 单元格
error_count = sheet.range('G7').value
error_rate = sheet.range('G8').formula = f"={error_count}/{word_count}" # 利用 Excel 公式计算错误率
# 将提取的数据放入一个DataFrame
df = pd.DataFrame([[project_id, start_date, error_data, translation_accuracy, word_count, error_rate]],
columns=["project_id", "start_date", "error_data", "translation_accuracy", "word_count", "error_rate"])
result.append(df)
# 将结果存储到数据库
store_data_in_db(df, engine)
wb.close()
except Exception as e:
print(f"Error processing file {filename} with xlwings: {e}")
# 将所有结果合并为一个DataFrame
result_df = pd.concat(result, ignore_index=True)
# 将结果写入一个新的Excel文件,使用 xlwings 进行更高级的文件处理,如添加样式
with xw.App(visible=False) as app:
new_wb = app.books.add()
new_sheet = new_wb.sheets[0]
new_sheet.range('A1').value = result_df
# 为表头添加样式
new_sheet.range('A1:F1').api.Font.Bold = True
new_sheet.range('A1:F1').color = (255, 0, 0) # 红色表头
new_wb.save(os.path.join(desktop_path, 'extracted_data_xlwings_complex.xlsx'))
new_wb.close()
print("批量提取、处理和存储完成,结果已保存为 'extracted_data_xlwings_complex.xlsx' 和存储在数据库中")
(三)代码解释:
store_data_in_db 函数:使用 sqlalchemy 库将数据存储到 PostgreSQL 数据库中,可灵活处理不同的数据库操作。
使用 xlwings 启动 Excel 应用程序,打开文件和工作表。
在提取 A1:A10 区域的数据时,利用 Excel 的函数(如 TRIM 和 SUBSTITUTE)进行文本清洗。
对于 E15:G20 区域的数据,使用 Excel 的 IF 函数检查数值有效性。
在计算错误率时,使用 Excel 的公式进行计算,利用 xlwings 直接将公式插入到相应单元格。
将数据存储到数据库时,使用 pandas 和 sqlalchemy 确保数据存储符合高级数据库特性。
存储数据到新的 Excel 文件时,使用 xlwings 对文件进行更高级的样式处理,如添加红色表头。
方案三:使用 pandas 和 matplotlib 进行数据处理和可视化
(一)实现思路:
- 利用
pandas
的强大功能,读取 Excel 文件中的多个工作表,并筛选出包含特定关键字的工作表。 - 对于每个筛选出的工作表,使用
pandas
的iloc
和loc
功能准确提取不同区域的数据。 - 针对不同的数据类型,使用
pandas
的内置方法进行数据清洗、转换和统计计算。 - 利用
matplotlib
库进行数据可视化,生成各种图表。 - 使用
json
库将最终数据存储为 JSON 格式。
(二)代码展示:
import os
import pandas as pd
import matplotlib.pyplot as plt
import json
def clean_text(text):
# 去除多余空格和特殊字符
cleaned_text = " ".join(text.split())
return cleaned_text
def process_text_data(df, column):
# 对文本数据进行分词处理
df[column] = df[column].apply(lambda x: clean_text(x).split())
def process_numeric_data(df, column):
# 计算平均值和标准差
mean_value = df[column].mean()
std_value = df[column].std()
print(f"Column {column} mean: {mean_value}, std: {std_value}")
return mean_value, std_value
def process_date_data(df, column):
# 将日期数据转换为统一的日期格式
df[column] = pd.to_datetime(df[column], errors='coerce')
# 计算日期之间的时间差
df['Time_Diff'] = df[column].diff()
def generate_visualizations(df):
# 生成柱状图:以某列数据为例
plt.figure(figsize=(10, 6))
df['Some_Numeric_Column'].plot(kind='bar')
plt.title('Numeric Column Distribution')
plt.xlabel('Index')
plt.ylabel('Value')
plt.savefig('numeric_column_distribution.png')
plt.close()
# 生成折线图:以日期数据为例
plt.figure(figsize=(10, 6))
df.set_index('Date_Column')['Time_Diff'].plot(kind='line')
plt.title('Time Difference Over Time')
plt.xlabel('Date')
plt.ylabel('Time Difference')
plt.savefig('time_difference_over_time.png')
# 生成饼图:以分类数据为例
plt.figure(figsize=(10, 6))
df['Category_Column'].value_counts().plot(kind='pie', autopct='%1.1f%%')
plt.title('Category Distribution')
plt.ylabel('')
plt.savefig('category_distribution.png')
# 获取桌面路径
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
# 初始化结果列表
results = []
# 遍历桌面目录下的所有.xlsx文件
for filename in os.listdir(desktop_path):
if filename.endswith('.xlsx'):
file_path = os.path.join(desktop_path, filename)
try:
xls = pd.ExcelFile(file_path)
for sheet_name in xls.sheet_names:
if "LQA_Data" in sheet_name:
df = pd.read_excel(file_path, sheet_name=sheet_name)
# 提取 A1:A10 区域的文本数据
base_info = df.iloc[:10, 0]
process_text_data(base_info, base_info.name)
# 提取 B20:D30 区域的详细数据
detailed_data = df.iloc[19:30, 1:4]
# 假设第二列是数字数据,进行处理
mean, std = process_numeric_data(detailed_data, detailed_data.columns[1])
detailed_data[f'{detailed_data.columns[1]}_Mean'] = mean
detailed_data[f'{detailed_data.columns[1]}_Std'] = std
# 提取以 "Summary_" 开头的列所在行的数据
summary_columns = [col for col in df.columns if col.startswith("Summary_")]
summary_data = df.loc[0, summary_columns]
process_date_data(summary_data.to_frame().T, summary_columns[0]) # 假设第一个是日期数据
# 组合提取的数据
combined_df = pd.concat([base_info, detailed_data, summary_data], axis=1)
results.append(combined_df)
# 生成可视化
generate_visualizations(combined_df)
except Exception as e:
print(f"Error processing file {filename}: {e}")
# 合并所有结果为一个 DataFrame
final_df = pd.concat(results, ignore_index=True)
# 将结果存储到 Excel 文件
final_df.to_excel(os.path.join(desktop_path, 'extracted_data_pandas_visualization.xlsx'), index=False)
# 将结果存储为 JSON 格式
with open(os.path.join(desktop_path, 'extracted_data_pandas_visualization.json'), 'w') as f:
json.dump(final_df.to_dict(orient='records'), f)
print("批量提取、处理、可视化和存储完成,结果已保存为 'extracted_data_pandas_visualization.xlsx' 和 'extracted_data_pandas_visualization.json'")
(三)代码解释:
clean_text
函数:用于去除文本中的多余空格和特殊字符。process_text_data
函数:对文本数据进行分词处理,将文本拆分成多个关键词。process_numeric_data
函数:计算数字数据的平均值和标准差,并将结果添加到数据中。process_date_data
函数:将日期数据转换为统一的日期格式,并计算时间差。generate_visualizations
函数:使用matplotlib
生成不同类型的可视化图表,包括柱状图、折线图和饼图。在遍历文件和工作表时:
- 对于每个文件,使用
pandas
的ExcelFile
功能读取文件,并筛选出包含 "LQA_Data" 的工作表。 - 使用
iloc
和loc
分别提取不同区域的数据。 - 对提取的数据根据数据类型进行不同的处理操作。
- 生成相应的可视化图表。
- 最终将数据存储到 Excel 文件和 JSON 文件中。
- 对于每个文件,使用
方案四:使用 PyExcelerate 和 Plotly 进行高性能数据处理和交互式可视化**
(一)实现思路:
- 使用
PyExcelerate
库进行 Excel 文件的快速读取和写入,提高性能。 - 对于数据的提取和处理,结合
pandas
的部分功能,确保数据的准确性和完整性。 - 利用
Plotly
库进行交互式数据可视化,允许用户在网页上进行操作和查看数据图表,提供更好的用户体验。 - 将最终的数据存储为 CSV 格式,方便数据的进一步处理和导入到其他系统。
(二)代码展示:
import os
import pandas as pd
import pyexcelerate
import plotly.express as px
def extract_data_from_sheets(file_path):
data_frames = []
workbook = pyexcelerate.Workbook(file_path)
for sheet_name in workbook.sheet_names():
if "LQA_Data" in sheet_name:
data = workbook.get_sheet_data(sheet_name)
# 假设数据存储在一个列表的列表中,将其转换为 DataFrame
df = pd.DataFrame(data[1:], columns=data[0])
# 提取不同区域的数据,这里仅为示例,根据实际情况调整
base_info = df.iloc[:10, 0]
detailed_data = df.iloc[19:30, 1:4]
summary_columns = [col for col in df.columns if col.startswith("Summary_")]
summary_data = df.loc[0, summary_columns]
combined_df = pd.concat([base_info, detailed_data, summary_data], axis=1)
data_frames.append(combined_df)
return pd.concat(data_frames, ignore_index=True)
def process_data(df):
# 这里可以添加各种数据处理逻辑,与之前的方案类似
# 例如,对文本数据进行清洗,对数字数据进行统计计算等
return df
def generate_interactive_visualizations(df):
# 生成交互式柱状图
fig = px.bar(df, x='Category_Column', y='Some_Numeric_Column', title='Interactive Numeric Column Distribution')
fig.write_html('interactive_numeric_column_distribution.html')
# 生成交互式折线图
fig = px.line(df, x='Date_Column', y='Time_Diff', title='Interactive Time Difference Over Time')
fig.write_html('interactive_time_difference_over_time.html')
# 获取桌面路径
desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop')
# 初始化结果列表
results = []
# 遍历桌面目录下的所有.xlsx文件
for filename in os.listdir(desktop_path):
if filename.endswith('.xlsx'):
file_path = os.path.join(desktop_path, filename)
try:
df = extract_data_from_sheets(file_path)
processed_df = process_data(df)
results.append(processed_df)
generate_interactive_visualizations(processed_df)
except Exception as e:
print(f"Error processing file {filename}: {e}")
# 合并所有结果为一个 DataFrame
final_df = pd.concat(results, ignore_index=True)
# 将结果存储到 CSV 文件
final_df.to_csv(os.path.join(desktop_path, 'extracted_data_pyexcelerate_plotly.csv'), index=False)
print("批量提取、处理、可视化和存储完成,结果已保存为 'extracted_data_pyexcelerate_plotly.csv'")
(三)代码解释:
extract_data_from_sheets
函数:使用PyExcelerate
读取 Excel 文件,并筛选出包含 "LQA_Data" 的工作表,将数据转换为pandas
的 DataFrame。process_data
函数:可以添加各种数据处理逻辑,类似于之前的方案。generate_interactive_visualizations
函数:使用Plotly
生成交互式的柱状图和折线图,并将其保存为 HTML 文件,方便用户在网页上查看。在遍历文件时:
- 使用
PyExcelerate
读取文件并提取数据。 - 对提取的数据进行处理。
- 生成交互式可视化图表。
- 将最终数据存储为 CSV 文件。
- 使用
小结
- 方案一:openpyxl
openpyxl 是一个基础但功能强大的库,在处理 Excel 文件的静态数据方面表现出色。通过结合 Python 的字符串和数字处理能力,我们可以对提取的数据进行复杂的清洗和检查操作。在存储数据时,我们可以使用 sqlite3 等库将数据存储到本地数据库中,适合小型项目或简单的数据分析需求。然而,它不能直接处理公式计算,对于需要复杂公式处理的情况,可能需要额外的计算逻辑。 - 方案二:xlwings
xlwings 提供了与 Excel 更紧密的集成,可以充分利用 Excel 的功能进行数据处理,如调用 Excel 的内置函数,这对于复杂的数据处理和公式计算非常有帮助。在存储数据方面,使用 sqlalchemy 库可以方便地将数据存储到各种类型的数据库中,并且可以实现高级的数据库操作。同时,它还可以对存储到 Excel 文件的数据进行高级的样式处理,适合需要高级 Excel 交互和复杂数据存储的场景。
通过这两种高级方案的结合使用,我们可以根据不同的项目和数据特点,灵活地选择最适合的工具和方法,完成从复杂的 Excel 文件中提取、处理和存储大量 LQA 数据的任务,确保整个评估过程的高效性和准确性,满足对不同语言和项目的精细化评估需求,为后续的质量管理和工作风格差异分析提供坚实的数据支持。 - 方案三:pandas 和 matplotlib
该方案充分利用了pandas
的强大功能进行数据处理和整合,同时使用matplotlib
生成静态的可视化图表。这种组合适合对数据进行复杂的分析和报告生成,特别是对于熟悉pandas
生态系统的用户来说,可以方便地进行数据操作和统计计算,同时生成标准的可视化报告。 - 方案四:PyExcelerate 和 Plotly
PyExcelerate
提供了高性能的 Excel 数据读取和写入功能,而Plotly
允许我们生成交互式的可视化图表,这种组合适合对性能有要求且需要为用户提供更好可视化体验的场景。通过将数据存储为 CSV 格式,还方便了数据的进一步流通和使用。