2023 年“泰迪杯”数据分析技能赛 A 题 档案数字化加工流程数据分析
2023 年“泰迪杯”数据分析技能赛 A 题 档案数字化加工流程数据分析
完整代码请私聊 博主
一、 背景
档案数字化是随着扫描、OCR、数字摄影、数据库、多媒体、存储等技术的发展而产生的一种新型档案信息处理技术,它把各种载体的档案资源转化为数字化档案信息,以数字化的形式存储,网络化的形式互相连接,利用计算机系统进行管理,形成一个有序结构的档案信息库。我国档案工作采取“存量数字化、增量电子化”的信息化战略。当前我国各行业的存量档案数量巨大,档案数字化的需求不断增加,档案数字化加工行业的市场规模呈现逐年增长的趋势。
二、 目标
对加工流程数据进行统计分析,并作可视化展示,便于管理人员及时了解档案加工处理动态。
- 统计档案数字化流程的耗时和进度情况。
- 统计操作人员的工作量和工作效率情况。
三、 案卷加工流程说明
- 加工流程按先后顺序分为以下几个工序:扫描、图像处理、自检全检、PDF 处理。
- 操作人员领取、提交案卷:一个操作人员可以胜任多个工序的工作。启动某个工序时,操作人员首先在系统上批量领取一定数量的加工任务,文件 data.xlsx 中的字段“dUPDATE_TIME”记录了每份案卷的领取时间;档案处理完成后,在系统上进行批量提交,文件 data.xlsx 中的字段“dNODE_TIME”记录了每份案卷的提交时间。当领取的案卷数量较多时,通常会在中午休息前或下午下班前提交已完成的部分案卷。允许操作人员在未完成已领取的任务前领取新任务。
- 工作效率按批进行计算,将同一批案卷的最后提交时间减去这批案卷的最早领取时间作为该批案卷的总耗时,以此计算该批案卷的平均耗时。所谓“批”是对同一个操作人员在同一个工序中,从领取第一份案卷开始,直到该操作人员在该工序中所有案卷都提交完成,在这段时间内处理的所有案卷。文件 data.xlsx 中的字段“sBatch_number”记录了批的编号。
2 - 文件 data.xlsx 中的字段“iNODE_STATUS”(工序状态)为 2,表明案卷已完成并提交,且不需要返工;该字段为 5,表明案卷经过返工,已完成并提交。
- 工作时间为周一至周六上午 8:30-12:00,下午 13:00-18:00,案卷的处理时间和操作人员的工作时长应去掉非工作时间。注意:实际工作中,可能有提前上岗或推迟下岗的情况。
四、 任务
data.xlsx 记录了某档案数字化加工单位 2020 年 7 月加工处理过程中各个工序的管理数据。请编程完成以下任务并撰写报告,在报告中详细描述各项任务的处理思路、过程及必要的结果。结果的模板文件在文件夹“result”中。
任务 1 数据预处理与统计
任务 1.1
统计完成四道工序的案卷数量,在报告中列出统计结果。汇总各案卷各工序的开始时间及各案卷的完成时长,以表 1 的格式将汇总结果保存到文件“result1_1.xlsx”中,同时在报告中列出案卷完成时长最长的三个案卷的结果。
注 1 每个案卷的完成时长是扫描、图像处理、自检全检三个工序的耗时之和,PDF 处理无需计算耗时,各工序的耗时是该工序的开始时间至结束时间的时长。
注 2 完成时长应去掉非工作时间(“三、案卷加工流程说明”第 5 条),单位:h,保留 3 位小数。

import pandas as pd
import numpy as np
df1_1 = pd.read_excel('data.xlsx')
df1_1.head()
df1_1 = df1_1[df1_1['iNODE_STATUS']==2]
df1_1['iNODE_STATUS'].count()
135143
data = pd.DataFrame(columns=['案卷号','扫描开始时间','扫描结束时间'])
data
data2 = pd.DataFrame(columns=['案卷号','图像处理开始时间','图像处理结束时间'])
data2
data3 = pd.DataFrame(columns=['案卷号','自检全检开始时间','自检全检结束时间'])
data3
data4 = pd.DataFrame(columns=['案卷号','PDF处理开始时间','PDF处理结束时间'])
data4
data['案卷号'] = df1_1['sARCH_ID']
data2['案卷号'] = df1_1['sARCH_ID']
data3['案卷号'] = df1_1['sARCH_ID']
data4['案卷号'] = df1_1['sARCH_ID']
data['扫描开始时间'] = df1_1[df1_1['iFLOW_NODE_NO']==1]['dUPDATE_TIME']
data['扫描结束时间'] = df1_1[df1_1['iFLOW_NODE_NO']==1]['dNODE_TIME']
data2['图像处理开始时间'] = df1_1[df1_1['iFLOW_NODE_NO']==2]['dUPDATE_TIME']
data2['图像处理结束时间'] = df1_1[df1_1['iFLOW_NODE_NO']==2]['dNODE_TIME']
data3['自检全检开始时间'] = df1_1[df1_1['iFLOW_NODE_NO']==3]['dUPDATE_TIME']
data3['自检全检结束时间'] = df1_1[df1_1['iFLOW_NODE_NO']==3]['dNODE_TIME']
data4['PDF处理开始时间'] = df1_1[df1_1['iFLOW_NODE_NO']==4]['dUPDATE_TIME']
data4['PDF处理结束时间'] = df1_1[df1_1['iFLOW_NODE_NO']==4]['dNODE_TIME']
data.dropna(inplace=True)
data.reset_index(inplace=True,drop=True)
data
data2.dropna(inplace=True)
data2.reset_index(inplace=True,drop=True)
data2
data3.dropna(inplace=True)
data3.reset_index(inplace=True,drop=True)
data3
data4.dropna(inplace=True)
data4.reset_index(inplace=True,drop=True)
data4
案卷号 扫描开始时间 扫描结束时间
0 托644031-册一 2020-07-01 09:17:22.313 2020-07-01 09:21:10
1 托644032-册一 2020-07-01 09:17:42.867 2020-07-01 09:21:17
2 托7181-册一 2020-07-01 08:39:08.370 2020-07-01 09:26:29
3 托7182-册一 2020-07-01 08:39:08.397 2020-07-01 09:26:36
4 托7183-册一 2020-07-01 08:39:08.420 2020-07-01 09:26:42
... ... ... ...
33823 托750599-册一 2020-07-30 15:27:57.460 2020-07-30 16:22:13
33824 托750600-册一 2020-07-30 15:28:27.477 2020-07-30 16:22:20
33825 托698742-册一 2020-07-30 13:10:03.703 2020-07-30 16:22:44
33826 托750590-册一 2020-07-30 15:27:57.380 2020-07-30 16:25:02
33827 托750591-册一 2020-07-30 15:27:57.390 2020-07-30 16:27:27
df = data.merge(data2)
df2 = df.merge(data3)
df3 = df2.merge(data4)
df3
df3['扫描开始时间'] = df3['扫描开始时间'].dt.floor('1s')
df3['扫描开始day'] = df3['扫描开始时间'].dt.day
df3['扫描结束day'] = df3['扫描结束时间'].dt.day
df3['图像处理开始day'] = df3['图像处理开始时间'].dt.day
df3['图像处理结束day'] = df3['图像处理结束时间'].dt.day
df3['自检全检开始day'] = df3['自检全检开始时间'].dt.day
df3['自检全检结束day'] = df3['自检全检结束时间'].dt.day
date_time = pd.to_datetime(df3['扫描开始时间'])
df3['扫描开始time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df3['扫描结束时间'])
df3['扫描结束time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df3['图像处理开始时间'])
df3['图像处理开始time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df3['图像处理结束时间'])
df3['图像处理结束time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df3['自检全检开始时间'])
df3['自检全检开始time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df3['自检全检结束时间'])
df3['自检全检结束time'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
def clean(sd,ed,st,et):
if sd == ed:
if 8.5<=et<=12:
return et-st
elif 13<=et<=18:
if 13<=st<=18:
return et-st
else:
return et-st-1
.....................................................
else:
if 8.5<=et<=12:
return (18-st)+(et-8.5)
else:
return (18-st)+(3.5+18-et)
df3['扫描时长'] = df3.apply(lambda x:clean(x['扫描开始day'],x['扫描结束day'],x['扫描开始time'],x['扫描结束time']),axis=1)
.......................................................
df3['完成时长'] = df3['扫描时长'].values+df3['图像时长'].values+df3['自检全检时长'].values
df3 = df3[['案卷号','扫描开始时间','扫描结束时间','图像处理开始时间',
'图像处理结束时间','自检全检开始时间','自检全检结束时间','PDF处理开始时间','PDF处理结束时间','完成时长']]
df3
案卷号 扫描开始时间 扫描结束时间 图像处理开始时间 图像处理结束时间 自检全检开始时间 自检全检结束时间 PDF处理开始时间 PDF处理结束时间 完成时长
0 托644031-册一 2020-07-01 09:17:22 2020-07-01 09:21:10 2020-07-01 15:01:56 2020-07-01 16:44:24 2020-07-08 08:35:26 2020-07-08 10:54:15 2020-07-10 15:05:18 2020-07-10 15:18:02 4.084722
1 托644032-册一 2020-07-01 09:17:42 2020-07-01 09:21:17 2020-07-01 15:01:56 2020-07-01 16:44:26 2020-07-08 08:35:26 2020-07-08 10:54:17 2020-07-10 15:05:18 2020-07-10 15:18:02 4.082222
2 托7181-册一 2020-07-01 08:39:08 2020-07-01 09:26:29 2020-07-02 10:47:50 2020-07-02 11:30:47 2020-07-02 11:31:18 2020-07-02 11:43:48 2020-07-03 08:49:13 2020-07-03 09:04:24 1.713333
3 托7182-册一 2020-07-01 08:39:08 2020-07-01 09:26:36 2020-07-02 10:47:50 2020-07-02 11:31:08 2020-07-02 11:31:18 2020-07-02 11:43:50 2020-07-03 08:49:13 2020-07-03 09:04:24 1.721667
4 托7183-册一 2020-07-01 08:39:08 2020-07-01 09:26:42 2020-07-02 13:37:15 2020-07-02 13:53:05 2020-07-02 15:07:37 2020-07-02 15:15:12 2020-07-03 08:49:13 2020-07-03 09:04:24 1.183056
... ... ... ... ... ... ... ... ... ... ...
33356 托750599-册一 2020-07-30 15:27:57 2020-07-30 16:22:13 2020-07-31 10:10:06 2020-07-31 10:21:15 2020-07-31 10:23:55 2020-07-31 13:35:57 2020-07-31 14:06:17 2020-07-31 14:28:47 3.290833
33357 托750600-册一 2020-07-30 15:28:27 2020-07-30 16:22:20 2020-07-30 16:52:21 2020-07-31 08:38:13 2020-07-31 10:24:12 2020-07-31 13:36:06 2020-07-31 14:06:17 2020-07-31 14:28:47 4.360833
33358 托698742-册一 2020-07-30 13:10:03 2020-07-30 16:22:44 2020-07-30 16:19:40 2020-07-30 16:22:29 2020-07-31 08:27:52 2020-07-31 10:09:09 2020-07-31 11:07:01 2020-07-31 11:21:18 4.946389
33359 托750590-册一 2020-07-30 15:27:57 2020-07-30 16:25:02 2020-07-31 10:10:05 2020-07-31 10:20:51 2020-07-31 10:23:54 2020-07-31 13:35:31 2020-07-31 14:06:17 2020-07-31 14:28:46 3.324444
33360 托750591-册一 2020-07-30 15:27:57 2020-07-30 16:27:27 2020-07-31 10:10:05 2020-07-31 10:20:52 2020-07-31 10:23:54 2020-07-31 13:35:32 2020-07-31 14:06:17 2020-07-31 14:28:46 3.365278
df3['完成时长'].sort_values( ascending=False)[:5]
df3.to_excel('result1_1.xlsx',index=None)
27963 30.738611
27962 30.718056
27961 30.700833
27960 30.674167
2292 30.617778
任务 1.2
统计需要返工的案卷数量及其占完工案卷总数的百分比,在报告中列出结果。
汇总返工案卷的返工工序和返工开始时间,以表 2 的格式将汇总结果保存到文件“result1_2.xlsx”中,同时在报告中列出返工案卷号“托 40606-册六”“托 40606-册七”“托 5901_1-册三”的结果。
注 未返工工序的时间为空。

df1_2 = pd.read_excel('data.xlsx')
df1_2.head()
.......................
rework_percentage = (rework_count / total_count) * 100
print("需要返工的案卷数量:", rework_count)
print("占完工案卷总数的百分比:", rework_percentage, "%")
需要返工的案卷数量: 797
占完工案卷总数的百分比: 0.5862880682654112 %
df1_2.reset_index(drop=True,inplace=True)
df1_2
fg = pd.DataFrame(columns=['案卷号','扫描','图像处理','自检全检','PDF 处理'])
fg['案卷号'] = df1_2['sARCH_ID']
fg['扫描'] = df1_2[df1_2['iFLOW_NODE_NO']==1]['dUPDATE_TIME']
fg['图像处理'] = df1_2[df1_2['iFLOW_NODE_NO']==2]['dUPDATE_TIME']
fg['自检全检'] = df1_2[df1_2['iFLOW_NODE_NO']==3]['dUPDATE_TIME']
fg['PDF 处理'] = df1_2[df1_2['iFLOW_NODE_NO']==4]['dUPDATE_TIME']
fg[(fg['案卷号']=='托40606-册六')|(fg['案卷号']=='托40606-册七')|(fg['案卷号']=='托5901_1-册三')]
fg.to_excel('result1_2.xlsx',index=None)
案卷号 扫描 图像处理 自检全检 PDF 处理
50 托40606-册六 NaT NaT 2020-07-07 10:58:05 NaT
51 托40606-册七 NaT NaT 2020-07-07 10:58:05 NaT
193 托5901_1-册三 NaT 2020-07-11 11:53:34 NaT NaT
任务 1.3
对自检全检工序,汇总每个操作人员的返工案卷数,计算其占该操作人员该工序工作总量的百分比,按百分比降序排列,以表 3 的格式将结果保存到文件“result1_3.xlsx”中,同时在报告中列出前三位操作人员的结果。结果保留 3 位小数,例如:返工案卷占比为 1%,在结果表中填写“1.000”。

操作人员ID count 返工案卷占比(%)
1 10 11579 0.341
2 12 8339 0.245
3 13 7224 0.213
任务 1.4
按工序分别统计完成案卷的数量、总耗时和平均耗时,以表 4 的格式将结果保存到文件“result1_4.xlsx”中,并在报告中列出结果。结果保留 3 位小数。
注 按工序计算总耗时,是该工序各个批次的案卷集最早开始时间至案卷集最晚结束时间之和,而不是各个案卷完成时长的总和。

工序 完成案卷的数量 总耗时(h) 平均耗时(h/卷)
0 扫描 33828 24.837 0.001
1 图像处理 33815 27.306 0.001
2 自检全检 33619 35.187 0.001
3 PDF 处理 33881 32.901 0.001
任务 1.5
按操作人员、工序统计工作时长、完成案卷的数量和每个案卷的平均耗时(h/卷),以表 5 的格式将结果按操作人员 ID 升序排列保存到文件“result1_5.xlsx”中,同时在正文中列出操作人员 ID“10”“33”“48”的结果。结果保留 3 位小数。
注 按操作人员、工序统计工作时长是按批进行的(“三、案卷加工流程说明”第 3条),应去除非工作时间(“三、案卷加工流程说明”第 5 条)。

df1_5 = pd.read_excel('data.xlsx')
date_time = pd.to_datetime(df1_5['dUPDATE_TIME'])
df1_5['开始'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
date_time = pd.to_datetime(df1_5['dNODE_TIME'])
df1_5['结束'] = date_time.dt.hour + date_time.dt.minute / 60 + date_time.dt.second / 3600
df1_5['工作时长(h)'] = round(abs(df1_5['结束']-df1_5['开始']),3)
result = df1_5[df1_5['iNODE_STATUS'] == 2]
result = result.groupby(['iUSER_ID','iFLOW_NODE_NO'],as_index=False).agg({'工作时长(h)':'sum','sARCH_ID':'nunique'})
result['每个案卷的平均耗时(h/卷)'] = round(result['工作时长(h)']/result['sARCH_ID'],3)
result = result.reset_index()
result = result.rename(columns={'iUSER_ID':'操作人员ID','iFLOW_NODE_NO':'工序','sARCH_ID':'完成案卷的数量'})
result = result.drop('index',axis=1)
result.to_excel('result1_5.xlsx',index=None)
result[(result['操作人员ID']==10)|(result['操作人员ID']==33)|(result['操作人员ID']==48)]
操作人员ID 工序 工作时长(h) 完成案卷的数量 每个案卷的平均耗时(h/卷)
3 10 1 4.951 9 0.550
4 10 2 0.422 9 0.047
5 10 3 20489.869 11501 1.782
25 33 1 6870.677 2300 2.987
31 48 2 10104.625 3579 2.823
任务 2 数据分析与可视化
读取数据
import pandas as pd
import matplotlib.pyplot as plt
import pyecharts
from pyecharts import options as opts
plt.rcParams['font.sans-serif'] = 'SimHei'
df2 = pd.read_excel('data.xlsx')
df2.head(4)
iID iPID uFILE_FLAG sARCH_ID sFLOW_NAME sNODE_NAME iNODE_STATUS iUSER_ID iWF_ID iWN_ID ... iFLOW_NODE_NO iPROC_USERID sPIC_SERVER_PATH sPDF_SERVER_PATH iARCH_TYPE sORDER_ARCH_ID dUPDATE_TIME dNODE_TIME dPROC_TIME sBatch_number
0 725975 186250 0B71F6DD-E7EC-4FB2-87C4-EB576B70F4C2 托644031-册一 不动产工作流 扫描 2 68 20 12 ... 1 NaN \\127.0.0.1\tlscan\tlscanfile \\192.168.1.253\tlscan\tlscanpdf 91 托00644031-册001 2020-07-01 09:17:22.313 2020-07-01 09:21:10 NaT 902
1 725976 186251 7B0B1758-12F7-4A44-91F9-96C5B9B2E6FD 托644032-册一 不动产工作流 扫描 2 68 20 12 ... 1 NaN \\127.0.0.1\tlscan\tlscanfile \\192.168.1.253\tlscan\tlscanpdf 91 托00644032-册001 2020-07-01 09:17:42.867 2020-07-01 09:21:17 NaT 902
2 725249 185781 94CC72C0-A6B4-4627-868E-EE3B423877C7 托7181-册一 不动产工作流 扫描 2 44 20 12 ... 1 NaN \\127.0.0.1\tlscan\tlscanfile \\192.168.1.253\tlscan\tlscanpdf 91 托00007181-册001 2020-07-01 08:39:08.370 2020-07-01 09:26:29 NaT 604
3 725250 185782 FA6E9CC1-5579-41D5-918B-6B9F0D3B2486 托7182-册一 不动产工作流 扫描 2 44 20 12 ... 1 NaN \\127.0.0.1\tlscan\tlscanfile \\192.168.1.253\tlscan\tlscanpdf 91 托00007182-册001 2020-07-01 08:39:08.397 2020-07-01 09:26:36 NaT 604
任务 2.1
计算并绘制每天不同工序完成案卷数量的簇状柱形图:x 轴表示时间,y 轴表示完成案卷的数量,用不同颜色标记不同工序。
df2_1 = df2[df2['iNODE_STATUS']==2]
df2_1
df2_1 = df2_1.groupby(['iFLOW_NODE_NO','dNODE_TIME']).size().reset_index(name='完成案卷的数量')
df2_1
df2_1['dNODE_TIME'] = df2_1['dNODE_TIME'].dt.date
df2_1
df2_1 = df2_1.groupby(['dNODE_TIME','iFLOW_NODE_NO'],as_index=False).agg({'完成案卷的数量':'sum'})
df2_1
x = df2_1['dNODE_TIME'].unique().tolist()
y1 = df2_1[df2_1['iFLOW_NODE_NO']==1]['完成案卷的数量'].tolist()
y2 = df2_1[df2_1['iFLOW_NODE_NO']==2]['完成案卷的数量'].tolist()
y3 = df2_1[df2_1['iFLOW_NODE_NO']==3]['完成案卷的数量'].tolist()
y4 = df2_1[df2_1['iFLOW_NODE_NO']==4]['完成案卷的数量'].tolist()
from pyecharts.charts import Bar
bar_1 = (
Bar(
init_opts=opts.InitOpts(bg_color='white',)
)
.add_xaxis(x)
.add_yaxis("扫描", y1, gap="0%")
.add_yaxis("图像处理", y2, gap="0%")
.add_yaxis("自检全检", y3, gap="0%")
.add_yaxis("PDF 处理", y4, gap="0%")
.set_global_opts(
title_opts=opts.TitleOpts(title="每天不同工序完成案卷数量的簇状柱形图"),
xaxis_opts=opts.AxisOpts(
type_="category",
boundary_gap=False,
axislabel_opts=opts.LabelOpts(rotate=80),
),
legend_opts=opts.LegendOpts(
type_='plain',
pos_left='80%',
pos_top='5%',
),
)
)
bar_1.render_notebook()

任务 2.2
计算并绘制各工序每天投入工作量(单位:人·小时)的多重折线图:x 轴表示时间,y 轴表示每天投入的工作量,用不同颜色标记不同工序。

任务 2.3
绘制每天各工序返工案卷数占当天返工案卷总数的百分比堆积面积图:x 轴表示时间,y 轴表示百分比,用不同颜色标记不同工序。

任务 2.4
对图像处理工序,汇总每个操作人员返工案卷数,计算其占该工序返工案卷总数的百分比,并按百分比进行排序,绘制饼图,其中排名第 10 位及以后的合并成一个扇区。

任务 3 领取提交模式分析
档案的数字化加工中,操作人员在某个工序中的正常领取提交模式是在相对集中的时间内领取若干案卷,全部加工完成后在相对集中的时间内按照领取的顺序提交该批案卷。但在现实中会出现更多类型的领取提交模式,例如操作人员有时会分多次领取案卷,处理完后一起集中提交;或在未完全提交已领取案卷的情况下又领取了新的案卷。出现这种情况的可能原因是,在处理已领取的案卷时发现这些案卷的处理难度低于平均难度,操作人员出于提高个人工作效率的考虑通过多次领取的方式“囤积”易处理案卷。
可通过可视化的方法分析案卷领取提交时序,例如在图 1 所示的桑基图中,每份案卷对应图中一条直线,左端点的纵坐标表示领取时间,右端点的纵坐标表示提交时间。

图 1(b)对应于分两次领取、分两次提交,不同的案卷集在处理时序上出现交叉的领取提交模式。也可以使用图 2 中的模式示意图来表达。

请根据文件 data.xlsx 的批次数据,通过可视化的方法分析批次内案卷的领取提交时序,总结有哪几种领取提交模式。对每一种模式给出一个实际例子,以表 6 的格式保存到文件“result3.xlsx”中,同时在报告中参照图 1 和图 2 的方式分别绘制两种不同的示意图。

略。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
附录 数据说明
数据文件 data.xlsx 中的每条记录对应于一个案卷某个工序的处理记录,其中的字段名及其含义如表 7 所示。

完整代码请私聊 博主
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)