【Lab】提取result的bits和Y-PSNR数据并整理到Excel

更新:使用openpyxl库直接将数据写入Excel中

注意:openpyxl是第三方库,如果没有安装。请命令行里键入pip install openpyxl

正则表达式提取数据

比如文件夹路径为C:\Users\Administrator\Desktop\mainbuilding33,其下有若干个文件夹,文件结构大概这样。

  1. └─new
  2. ├─QP22
  3. └─result
  4. result12_11.txt
  5. result1_2.txt
  6. result3.txt
  7. result3_0.txt
  8. result3_12.txt
  9. result3_4.txt
  10. result4_22.txt
  11. result4_5.txt
  12. result5_1.txt
  13. result5_8.txt
  14. ├─QP27
  15. └─result
  16. result12_11.txt
  17. result1_2.txt
  18. result3.txt
  19. result3_0.txt
  20. result3_12.txt
  21. result3_4.txt
  22. result4_22.txt
  23. result4_5.txt
  24. result5_1.txt
  25. result5_8.txt
  26. └─QP32
  27. └─result
  28. result12_11.txt
  29. result1_2.txt
  30. result3.txt
  31. result3_0.txt
  32. result3_12.txt
  33. result3_4.txt
  34. result4_22.txt
  35. result4_5.txt
  36. result5_1.txt
  37. result5_8.txt

其中new为采用的方法,这样的文件夹在C:\Users\Administrator\Desktop\mainbuilding33下有多个,我只是给出了new。每个方法文件夹下面都有QP22、QP27、QP32三个文件夹,其下都有一个result文件夹,再往下就是结果集了,以txt格式保存的。

首先要将路径切换到要处理的*.txt目录下。这里用os.walk(path)函数可以递归遍历指定路径下的所有文件,再使用new_path = os.path.join(path, folder)拼合成绝对路径就行了。

先看下最终录入表格中的数据。这个文件就是使用该程序自动生成的。表格标题newreference1就是方法文件夹,QP32、QP27、QP22是方法文件夹下的文件夹。通常来说,三个QP文件夹下的txt文件个数和文件名都相同(看上面的文件结构)。只是里面的的数据有所不同。比特率和psnr是我们需要的数据。注意到第一列的数字,它是txt文件名下划线右边的数字部分,按照从小到大的顺序录入数据。好了知道了要求,可以开始干活了。

  • 按照Excel要求需要把下划线后的数字按从小到大的顺序排列。文件命名格式是这样的: \d+_\d+.txt,用正则表达式的方式\d+代表至少一个数字。按排序后的顺序依次打开文件。
  • 按上述顺序打开文件后,提取某行的两个信息。其中有一个文件是\d+.txt的命名格式,为根节点文件,需要单独处理。每打开一个文件获取到数据后立刻存入到列表中,这样保证了提取到的数据也保持顺序。

这是\d+_\d+.txt类型的文件所需提取的部分。

下面是\d+.txt类型(根节点)文件里面要提取的部分。

这两个数据对应的模式为

  1. found_rate = re.findall(r'POC\s+4.*?(\d+).bits', text)
  2. found_psnr = re.findall(r'POC\s+4.*?Y\s+(.*?).dB', text)

re.findall返回列表,而提取到的数据只有一个,所以若是要取得列表里的数据,加上索引0

  1. rate = found_rate[0]
  2. psnr = found_psnr[0]

采用列表存这些数据是肯定的,因为这能保证存入的数据和文件打开的顺序一致。但是文件夹层次有点复杂,for循环中很容易导致列表被重新初始化。所以决定采用dict即字典的数据结构存储数据。用文件夹名给dict指定key,value里存数据。这个dict应该是全局的。大概就是这样的,order是表格的第一列数字,表示文件名下划线的右边的数字。psnrrate也是列表。因为按顺序打开文件,故这些数据已经有序。

  1. {'new_QP22': {'order': ['0', '2', '5', '7', '8', '9'],
  2. 'psnr': ['40.7843',
  3. '42.0684',
  4. '40.6155',
  5. '41.2164',
  6. '41.5846',
  7. '43.8196'],
  8. 'rate': ['754592',
  9. '1028656',
  10. '817128',
  11. '801176',
  12. '852344',
  13. '1019080']},
  14. 'new_QP27': {'order': ['0', '2', '5', '7', '8', '9'],
  15. 'psnr': ['36.2940',
  16. '37.0931',
  17. '36.0402',
  18. '36.5335',
  19. '36.8345',
  20. '38.9266'],
  21. 'rate': ['385512',
  22. '575288',
  23. '413672',
  24. '448480',
  25. '424440',
  26. '610656']},
  27. 'new_QP32': {'order': ['0', '2', '5', '7', '8', '9'],
  28. 'psnr': ['34.8091',
  29. '33.9552',
  30. '34.5914',
  31. '34.9151',
  32. '34.7430',
  33. '34.9727'],
  34. 'rate': ['276680',
  35. '319760',
  36. '301488',
  37. '330904',
  38. '258832',
  39. '328872']}
  40. }

数据获取到了,只差写入Excel中了。

使用openpyxl将数据写到Excel

这个库的使用就不再这里介绍了,可以看openpyxl文档,也可以看我之前的一篇博客。由于全部数据我都获取到了,使用openpyxl的一个append方法可以将数据成行成行写入表格中。但是根据表格要求,每行的数据来自于不同的上面dict中的不同key,这意味着不做任何处理写入数据,得到的表格和要求的正好是行列对调了的(想象一下矩阵的转置)

所以需要使用到Python内置的zip函数,将这个“矩阵”先转置再写入,就能得到上面Excel的样子。

全部写入后还做了一些额外的工作。

比如先将每个单元格的字体都变成了等线,为了使数据显示不那么紧凑,增加了每个单元格的列宽。同时还合并了一些单元格,加粗或者倾斜了某些单元格的字体。那么问题来了,当全部数据写入完毕的情况下,会有好几个Table。怎么知道那些应该合并的数据位于哪一行呢?

new这个表格标题的单元格合并为例(毕竟找到这行,往下数1到2行就能定位QP和比特率/psnr那些行了)。首先第一行必然就是第一个表格标题的行号。怎么确定下面几个表格标题的行号呢?

这是有规律可循的,一个表格是由标题、下面的一行比特率/psnr、再下一行QP、程序里加上的两行空行共5行。再加上方法文件加下任意一个QP文件夹(上面有说到里面的文件个数相同)下的txt文件的个数。假如new方法下任一QP文件夹里有6个txt文件,因为第1行是第一个表格的标题行。这样可以推算处下一个表格的标题行号为

  1. 1 + 5 + 6 = 12

没错真的是第12行。假如所有方法文件夹下的文件个数用列表表示为[6, 8, 8]分别加上5得到[11, 13, 13],再在索引为0的地方插入1,表示第一个表格的标题行号。得到[1, 11, 13, 13],最后将这里面的数两两相加。即a[0] + a[1] 放入a[1]中(不要动a[0]=1 ,第一行是第一个表格的标题这是肯定的),然后将新的a[1]与a[2]相加放入a[2]中...用代码表示如下。为了防止脚标越界,故下面的代码中有减去1。

  1. for i in range(len(step_list) - 1):
  2. step_list[i + 1] = step_list[i] + step_list[i + 1]

这样之后列表中的数据为[1, 12, 25, 38],很好,就是这些位置。

代码中的step_list就是存了这些信息。

最后,打开那么多文件,有可能某个文件是空的,没有数据,这就导致re.findall()返回[]空列表,不加判断取脚标[0]会发生脚标越界错误。所以程序中假如了非空判断,当某个文件数据缺失时,提醒用户去异常信息中给出的路径查看该问题文件是否正常。

  1. found_rate = re.findall(r'POC\s+0.*?(\d+).bits', text)
  2. found_psnr = re.findall(r'POC\s+0.*?Y\s+(.*?).dB', text)
  3. # 如果没有数据,抛出异常
  4. if not found_rate or not found_psnr:
  5. raise Exception(f'数据缺失!请前往{os.path.join(current_path, filename)}查看')

还有一点,代码中某些列表中会看到很多None,表示写入excel中时,这个单元格不写数据。但还是必须的,为了整行写入,作为占位存在。

好了,上代码

  1. import os
  2. import re
  3. import pprint
  4. from openpyxl import Workbook
  5. from openpyxl.styles import Font, Alignment
  6. from openpyxl.utils import get_column_letter
  7. # TODO 1.改成自己的路径,r表示原始字符串,保留
  8. original_path = r'C:\Users\Administrator\Desktop\mainbuilding33'
  9. def find_data(path):
  10. """
  11. 找到每个result文本里面的rate和psnr_Y数据,存到一个字典中
  12. :param path: 待处理数据的文件夹路径
  13. :return: data_dict 数据存放在该字典
  14. """
  15. data_dict = {}
  16. for current_path, subfolders, filesname in os.walk(path):
  17. # 不是空列表才执行以下
  18. if filesname:
  19. # 找出所有根节点文件
  20. root_list = re.findall(r'result\d+.txt', str(filesname))
  21. # 排序,key参数指定对'[\d].txt'里靠近txt的数字排序.这里file_name为列表的每个子项
  22. # 由于字符串的排序方法会先比较第一个数字,再比较下一个,所以会导致'10'比'2'小,需转型为int来比较
  23. sorted_file_list = sorted(filesname, key=lambda file_name: int(re.findall(r'(\d+).txt', file_name)[0]))
  24. # excel中A列的数据
  25. order_data = [i for i in re.findall(r'(\d+).txt', str(sorted_file_list))]
  26. # 方法名
  27. method = current_path.split('\\')[-3]
  28. # QP名
  29. qp = current_path.split('\\')[-2]
  30. # 用来存放每个文件中提取的psnr和rate
  31. rate_list, psnr_list = [], []
  32. for filename in sorted_file_list:
  33. with open(os.path.join(current_path, filename)) as f:
  34. # 读取全部内容
  35. text = f.read()
  36. # 特殊处理根节点文件
  37. if filename in root_list:
  38. # (\d+)为捕获组,只会返回括号里面的内容
  39. found_rate = re.findall(r'POC\s+0.*?(\d+).bits', text)
  40. found_psnr = re.findall(r'POC\s+0.*?Y\s+(.*?).dB', text)
  41. # 如果没有数据,抛出异常
  42. if not found_rate or not found_psnr:
  43. raise Exception(f'数据缺失!请前往{os.path.join(current_path, filename)}查看')
  44. # 如果抛出异常直接终止程序,if执行了这下面的肯定执行不了,故无需else语句
  45. rate = found_rate[0]
  46. psnr = found_psnr[0]
  47. rate_list.append(rate)
  48. psnr_list.append(psnr)
  49. # 其他文件
  50. else:
  51. found_rate = re.findall(r'POC\s+4.*?(\d+).bits', text)
  52. found_psnr = re.findall(r'POC\s+4.*?Y\s+(.*?).dB', text)
  53. if not found_rate or not found_psnr:
  54. raise Exception(f'数据缺失!请前往{os.path.join(current_path, filename)}查看')
  55. rate = found_rate[0]
  56. psnr = found_psnr[0]
  57. rate_list.append(rate)
  58. psnr_list.append(psnr)
  59. # 所有数据收集完毕,存入字典并返回
  60. data_dict[method + '_' + qp] = {'rate': rate_list, 'psnr': psnr_list, 'order': order_data}
  61. return data_dict
  62. def save_to_excel(filename=None):
  63. """
  64. 将提取到的rate和psnr数据写入excel中
  65. 可以自定义保存的路径以及文件名,如C:\\Users\\Administrator\\Desktop\\abc.xlsx'
  66. :param filename:目录及文件名
  67. """
  68. basename = os.path.basename(original_path)
  69. # 新建一个工作表
  70. wb = Workbook()
  71. # 获取当前活动的工作表
  72. sheet = wb.active
  73. # 修改工作表名称
  74. sheet.title = basename
  75. data = find_data(original_path)
  76. # 用来定位表格标题的行号
  77. step_list = []
  78. method = None
  79. for key in data.keys():
  80. # 遇到方法相同的情况跳过,保证只写入一次, 此为表格标题的那行
  81. if key.split('_')[0] != method:
  82. # 写入一次后,值更新
  83. step_list.append(len(data[key]['order']) + 5)
  84. method = key.split('_')[0]
  85. # 换成32在前的顺序
  86. qp_list = sorted(set([qp.split('_')[1] for qp in data.keys()]), reverse=True)
  87. # 一行共两个QP32、QP27、QP22
  88. qp_list.extend(qp_list)
  89. qp_list.insert(0, None)
  90. data_list = [[None, method],
  91. [None, '比特率', None, None, 'psnr'],
  92. qp_list]
  93. k32 = method + '_' + qp_list[1]
  94. k27 = method + '_' + qp_list[2]
  95. k22 = method + '_' + qp_list[3]
  96. rows = zip(data[k32]['order'], data[k32]['rate'], data[k27]['rate'], data[k22]['rate'],
  97. data[k32]['psnr'], data[k27]['psnr'], data[k22]['psnr'])
  98. for row in rows:
  99. # 数据是文本格式,转为数字写入
  100. data_list.append(list(map(float, row)))
  101. # 增加行两空行
  102. data_list.append([])
  103. data_list.append([])
  104. # 将每一行写入excel
  105. for row in data_list:
  106. sheet.append(row)
  107. # 全局单元格设置
  108. # 每个单元格的字体设置
  109. for row in sheet.rows:
  110. for cell in row:
  111. cell.font = Font(name='等线')
  112. # 为了使数据不那么紧凑,增加每列的列宽。列要用到字母,get_column_letter(i)转换
  113. # 都是从1开始数的,注意range
  114. for i in range(1, sheet.max_column + 1):
  115. sheet.column_dimensions[get_column_letter(i)].width = 14
  116. # 针对需要合并的单元格
  117. step_list.insert(0, 1)
  118. for i in range(len(step_list) - 1):
  119. step_list[i + 1] = step_list[i] + step_list[i + 1]
  120. # step_list最后一个数值是将要写入的最新一行的行号。暂时用不到,所以减1
  121. for row_num in step_list[:-1]:
  122. # 1. 设置表格大标题
  123. start_index = 'B' + str(row_num)
  124. end_index = get_column_letter(sheet.max_column) + str(row_num)
  125. sheet[start_index].font = Font(name='等线', size=24, italic=True, bold=True)
  126. sheet.merge_cells(start_index + ':' + end_index)
  127. sheet.row_dimensions[row_num].height = 36
  128. sheet[start_index].alignment = Alignment(horizontal='center', vertical='center')
  129. # 合并rate,合并psnr
  130. rate_start_index = 'B' + str(row_num + 1)
  131. rate_end_index = get_column_letter((sheet.max_column - 1) / 2 + 1) + str(row_num + 1)
  132. psnr_start_index = get_column_letter((sheet.max_column - 1) / 2 + 2) + str(row_num + 1)
  133. psnr_end_index = get_column_letter(sheet.max_column) + str(row_num + 1)
  134. sheet.row_dimensions[row_num + 1].height = 20
  135. sheet.merge_cells(rate_start_index + ':' + rate_end_index)
  136. sheet.merge_cells(psnr_start_index + ':' + psnr_end_index)
  137. sheet[rate_start_index].font = Font(name='等线', bold=True)
  138. sheet[psnr_start_index].font = Font(name='等线', bold=True)
  139. sheet[rate_start_index].alignment = Alignment(horizontal='center', vertical='center')
  140. sheet[psnr_start_index].alignment = Alignment(horizontal='center', vertical='center')
  141. # 3. QP那行.
  142. # sheet.rows和sheet.columns是生成器,没有下标,先转为list
  143. for qp_cell in list(sheet.rows)[row_num + 1]:
  144. qp_cell.alignment = Alignment(horizontal='right', vertical='center')
  145. # 用户可以自定义文件名和存放的目录,若是没有填入参数,采用如下默认方案
  146. if filename is None:
  147. # 默认当前工作目录和默认的文件名
  148. filename = basename + '.xlsx'
  149. wb.save(filename)
  150. if __name__ == '__main__':
  151. # 2.可以一个填入参数,自定义保存的路径以及文件名
  152. # 如'C:\\Users\\Administrator\\Desktop\\abc.xlsx'
  153. save_to_excel()

运行结果就是直接生成Excel,一键运行啥都不用做了。不过最好还是打开稍微检查下,有错误了交到Boss那儿后果不太好。

旧的处理方法以及代码

这时以前写的,只是提取到了txt中,还是得手动赋值整列数据到excel中。上面的可以说最终版,十分方便。这个还是麻烦了。但是本着记录自己的学习过程,还是保留在博客中

方法很简单,每打开一个文件提获取到数据后,立刻写入输出文件中,这样也保证了提取到的数据也保持顺序。

正则表达式提取即可。

  1. import os
  2. import re
  3. # 1. 改成自己的路径,r表示原始字符串,保留
  4. original_path = r'C:\Users\Administrator\Desktop\mainbuilding33'
  5. def find_save(file_path, filename, root_list, out):
  6. with open(file_path) as f:
  7. # 读取全部内容
  8. text = f.read()
  9. # 特殊处理根节点文件
  10. if filename in root_list:
  11. # (\d+)为捕获组,只会返回括号里面的内容
  12. rate = re.findall(r'POC\s+0.*?(\d+).bits', text)[0]
  13. psnrY = re.findall(r'POC\s+0.*?Y\s+(.*?).dB', text)[0]
  14. out.write(rate + '\t\t' + psnrY + '\n')
  15. # 其他文件
  16. else:
  17. rate = re.findall(r'POC\s+4.*?(\d+).bits', text)[0]
  18. psnrY = re.findall(r'POC\s+4.*?Y\s+(.*?).dB', text)[0]
  19. out.write(rate + '\t\t' + psnrY + '\n')
  20. def run(path):
  21. filename = path.split('\\')[-1] + '.txt'
  22. # 输出文件
  23. with open(filename, 'w', encoding='utf-8') as f:
  24. for current_path, subfolders, filesname in os.walk(path):
  25. # 不是空列表才执行以下
  26. if filesname:
  27. # 排序,key参数指定对'[\d].txt'里靠近txt的数字排序.这里file_name为列表的每个子项
  28. # 由于字符串的排序方法会先比较第一个数字,再比较下一个,所以会导致'10'比'2'小,需转型为int来比较
  29. sorted_file_list = sorted(filesname, key=lambda file_name: int(re.findall(r'(\d+).txt', file_name)[0]))
  30. special_list = re.findall(r'result\d+.txt', str(filesname))
  31. method_name = current_path.split('\\')[-3]
  32. qp_name = current_path.split('\\')[-2]
  33. f.write('\n')
  34. f.write('*' * 6 + method_name + ' ' + qp_name + '*' * 6 + '\n')
  35. f.write('Rate' + '\t\t' + 'PSNR' + '\n\n')
  36. for file in sorted_file_list:
  37. find_save(os.path.join(current_path, file), file, special_list, f)
  38. if __name__ == '__main__':
  39. run(original_path)

结果如下:

很好,两列数据直接复制到Excel,OK!


by @sunhaiyu

2017.6.20

【Lab】提取result的bits和Y-PSNR数据并整理到Excel的更多相关文章

  1. chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法[bubuko.com]

    chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法,原文:http://bubuko.com/infodetail-328671.html 默认情况下如下图 Y轴并不是从0开始 ...

  2. Highcharts属性与Y轴数据值刻度显示Y轴最小最大值

    Highcharts 官网:https://www.hcharts.cn/demo/highcharts Highcharts API文档:https://api.hcharts.cn/highcha ...

  3. Innovus Lab和Lab Guide下载地址 | Innovus教程 - Flow系列 - 数据准备

    本文转自:自己的微信公众号<集成电路设计及EDA教程> <Innovus Lab和Lab Guide下载地址 |    Innovus教程 - Flow系列 - 数据准备>   ...

  4. 通过Python提取10000份log中的产品数据

    一.背景 协助产品部门在10000份产品log信息中提取产品的SN号.IMEI号.ICCID号到Excel表格中. 1.l原始的og内容: 2.提取后的Excel表格: 二.实现 1.思路 a.for ...

  5. Jmeter 提取http请求返回值里json数据参数化方法

    第三方插件下载地址:http://jmeter-plugins.org/downloads/all/ 插件下载后解压:找到JMeterPlugins-Extras.jar,把JMeterPlugins ...

  6. Echarts在手机端y轴数据过大,显示不全

    解决办法: 减少y轴的margion,和格式化y轴 myChart.setOption({ ..., yAxis: { axisLabel: { margin: , formatter: functi ...

  7. Chart.js Y轴数据以百分比展示

    新手一枚,解决的问题喜欢记录,也许正好有人在网上迷茫的百度着.-0- 最近使用Chart.js做折线图的报表展示,直接显示整数啥的很好弄毕竟例子直接在哪里可以用,百分比就没办法了.百度慢慢汲取营养,虽 ...

  8. 批量提取图片主要3个颜色匹配中文名字并写入到excel设置对应颜色的背景

    from gevent import monkey monkey.patch_all() import gevent from haishoku.haishoku import Haishoku im ...

  9. echarts使用记录(三):x/y轴数据和刻度显示及坐标中网格显示、格式化x/y轴数据

    1.去掉坐标轴刻度线,刻度数据,坐标轴网格,以Y轴为例,同理X轴 xAxis: [{ type: 'category', axisTick: {//决定是否显示坐标刻度 alignWithLabel: ...

随机推荐

  1. Python: 作图

    在python中实现数据的可视化,也即作图,一般是依赖matplotlib宏包实现的.但常见的代码中都是加载pylab,是不是这里写错了呀?其实pylib只是matplotlib的一个模块,只是被做成 ...

  2. Reids命令解析-RENAME

    有一天开发突然照过来问,维萨我这个Redis实例这么慢呢?为什么这么慢,于是连上实例SLOWLOG 一看,这些慢日志都是大部分是RENMAE操作导致的,可是为什么RENAME操作会慢呢?不就是改个名字 ...

  3. oracle学习笔记(2)-基本术语

    oracle基本术语 先上图. 相当粗糙的一个图,可能有些地方不够精细,大致结构基本是对的. 逻辑结构上从大到小的依次为文件(file)->表空间(tablespace)->段(segme ...

  4. CSS3学习系列之盒样式(二)

    text-overflow属性 当通过把overflow属性的属性值设定为"hidden"的方法,将盒中容纳不下的内容隐藏起来时,如果使用text-overflow属性,可以在盒的 ...

  5. jQuery实现按Enter键触发事件

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  6. svn文件图

  7. 【LeetCode】110. Balanced Binary Tree

    题目: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced bin ...

  8. Python读入CIFAR-10数据库

    CIFAR-10可以去http://www.cs.toronto.edu/~kriz/cifar.html下载(记得下载python格式) CIFAR-10数据组成: 训练集和测试集分别有50000和 ...

  9. H5学习第二周

    怎么说,在各种感觉中h5学习的第二周已经过来了,先总结一下,感觉学习h5是一件让我爱恨交加的事,学会一些新的知识并把它成功运行出来的时候是非常激动和兴奋的,但是有时候搞不懂一个标签或者属性的时候,就有 ...

  10. Struts2请求参数合法性校验机制

    在Action中通过代码执行数据校验 请求参数的输入校验途径一般分两种:客户端校验 :通过JavaScript 完成 (jquery validation插件),目的:过滤正常用户的误操作. 服务器校 ...