组里面做脑电采集了一些数据,数据以.dat文件存储,比较杂乱,数据形式为:
- 第一部分:信息行。以类似字典(dict)的形式存储,只占用1行,按位计算列数500以上。实际上读取时会读取为str型,需要在取出后转为dict
- 第二部分:数据体。以空格为分隔符,每行4个数值,float类型。行数不定。取出后也需从str列表转为float列表
- 第三部分:参数行。与第一部分类似,在数据体后面,由于格式化不佳,可能会占用多行,按位计算列数1000以上
下面对每一部分进行记录
1.采用的包和地址准备:
import csv
import os
import json
input_file_dir = 'D:\\Desktop\\脑电机器1\\dat\\'
output_file_dir = 'D:\\Desktop\\脑电机器1\\csv\\'
2.读取信息头
记input_file为输入的文件,以utf-8-sig编码打开文件,后续以同样方式写入,用readlines()按行读取为一个列表
fr = open(input_file, encoding='utf-8-sig', errors='ignore')
array = fr.readlines()
# 读取信息头
str_mat = array[0] # 取出字符串格式的字典
# print(type(str_mat))
dict_mat = json.loads(str_mat)
with open(file=output_file, mode='w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
取出第一行,使用json.loads()将读出的str类型列表,转换为字典(注意!此列表在写入时已经满足了字典的格式,如:{"a":A, "b": B})
使用csv.DictWriter()向csv文件中写入字典格式的数据,writer.writerow()表示向输出文件中按行写入,另外,采用with open是为了简化掉最后的close环节
3.读取数据体
由于数据体的行数不确定,因此定义了data_length作为数据区的截止行数(注意是截止时的行数,不是数据区的行数)。注意到每个文件都有一行信息头,因此数据体实际是从第二行开始的。这里采用了 计算最后的参数有多少行 来计算数据体的行数,用param_lines记录参数或信息行数。这里是采取了一点投机取巧的方式,由于数据体列数只有15列左右,而前后的参数行和信息头的列数都在数百以上,因此以列数作为判断方式,超过100即为参数行或信息头。
分类了三种情况:末尾参数仅1行(param_lines=1, 正常情况NORMAL)、末尾参数2行及以上(param_lines>=2, 末尾多参数行MULTI_PARAM)、末尾没有参数行(param_lines=0, NO_PARAM),利用isNormal来记录情况。
# 读取数据区
# 数据区结束后最多出现了两排参数
# 因此需要特别读取数据区长度
# 正常情况
length = len(array)
isNormal = "NORMAL"
# 计算参数行数量
param_lines = 0 # 参数行初始化
for row in range(0, length):
if len(array[row]) > 100:
param_lines += 1
# 数据情况标注
# 注意至少有一行参数行,即第一行
data_length = 0 # 初始化数据区截止行数
if param_lines >= 3: # 判断倒数第二行是否为参数
data_length = length - param_lines + 1 # 注意第一行也被计算进去,多减了需要加1
isNormal = "MULTI_PARAM"
elif param_lines == 1:
data_length = length
isNormal = "NO_PARAM"
elif param_lines == 2:
data_length = length - param_lines + 1
isNormal = "NORMAL"
for row in range(1, data_length):
data_mat1 = array[row]
data_str_list = data_mat1.split()
# print(data_str_list)
# print(type(data_str_list))
data_float_list = list(map(float, data_str_list))
with open(file=output_file, mode='a', newline='') as f:
writer = csv.writer(f)
# writer.writeheader()
writer.writerow(data_float_list)
NORMAL和MULTI_PARAM情况下,数据体的截止行:总行数-参数&信息行数(param_lines)+1(注意是从0开始计);在NO_PARAM情况下,数据体的截止行就是文件最后一行。可以考虑合并优化这一部分代码,但为了贴合数据本身情况,写成了三种情况。
数据用.split()函数进行分隔,输出为列表。
4.读取参数行
读取参数行时,虽然看上去数据已经按照字典格式写好了,但是读取出来实际上是str型,需要进行转换。采用json.loads()即可将已经格式化好的str类型列表转换为字典。注意写入时open函数的mode应为'a'
在NORMAL情况下,末尾的参数行应该只有一行,所以取出最后一行即可;在MULTI_PARAM情况下,注意初始化存储用的字典列表dict_mat()={},从参数行开始行的序数(注意从0开始计)即总行数-参数&信息行+1 到 总行数-1,遍历所有参数行进行写入,用.update()将字典列表取出为单独的字典格式(这里update实际上最初是合并&更新字典的作用);在NO_PARAM情况下,直接跳出函数。
# 读取参数区
str_mat_array = {} # 存储字符串格式的字典
dict_mat_array = {} # 存储字典
if isNormal == "NORMAL":
str_mat = array[length - 1] # 取出字符串格式的字典
# print(str_mat)
dict_mat = json.loads(str_mat)
# print(dict_mat)
with open(file=output_file, mode='a', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
elif isNormal == "MULTI_PARAM":
i = 0
dict_mat = {}
for row in range(length-param_lines+1, length-1):
str_mat_array[i] = array[row]
# print(str_mat_array[i])
dict_mat_array[i] = json.loads(str_mat_array[i])
dict_mat.update(dict_mat_array[i])
i += 1
with open(file=output_file, mode='a', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
elif isNormal == "NO_PARAM":
return True
5.完整代码
以上的部分合并为函数dat2csv(,),并加入对文件的批处理,完整代码如下
import csv
import os
import json
input_file_dir = 'D:\\Desktop\\脑电机器1\\dat\\'
output_file_dir = 'D:\\Desktop\\脑电机器1\\csv\\'
def dat2csv(input_file, output_file):
"""
data转csv核心函数
:param input_file: 输入文件全名
:param output_file: 输出文件全名
:return:
"""
fr = open(input_file, encoding='utf-8-sig', errors='ignore')
array = fr.readlines()
# 读取信息头
str_mat = array[0] # 取出字符串格式的字典
# print(type(str_mat))
dict_mat = json.loads(str_mat)
with open(file=output_file, mode='w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
# 读取数据区
# 数据区结束后最多出现了两排参数
# 因此需要特别读取数据区长度
# 正常情况
length = len(array)
isNormal = "NORMAL"
# 计算参数行数量
param_lines = 0 # 参数行初始化
for row in range(0, length):
if len(array[row]) > 100:
param_lines += 1
# 数据情况标注
# 注意至少有一行参数行,即第一行
data_length = 0 # 初始化数据区截止行数
if param_lines >= 3: # 判断倒数第二行是否为参数
data_length = length - param_lines + 1 # 注意第一行也被计算进去,多减了需要加1
isNormal = "MULTI_PARAM"
elif param_lines == 1:
data_length = length
isNormal = "NO_PARAM"
elif param_lines == 2:
data_length = length - param_lines + 1
isNormal = "NORMAL"
for row in range(1, data_length):
data_mat1 = array[row]
data_str_list = data_mat1.split()
# print(data_str_list)
# print(type(data_str_list))
data_float_list = list(map(float, data_str_list))
with open(file=output_file, mode='a', newline='') as f:
writer = csv.writer(f)
# writer.writeheader()
writer.writerow(data_float_list)
# 读取参数区
str_mat_array = {} # 存储字符串格式的字典
dict_mat_array = {} # 存储字典
if isNormal == "NORMAL":
str_mat = array[length - 1] # 取出字符串格式的字典
# print(str_mat)
dict_mat = json.loads(str_mat)
# print(dict_mat)
with open(file=output_file, mode='a', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
elif isNormal == "MULTI_PARAM":
i = 0
dict_mat = {}
for row in range(length-param_lines+1, length-1):
str_mat_array[i] = array[row]
# print(str_mat_array[i])
dict_mat_array[i] = json.loads(str_mat_array[i])
dict_mat.update(dict_mat_array[i])
i += 1
with open(file=output_file, mode='a', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=dict_mat.keys())
writer.writeheader()
writer.writerow(dict_mat)
elif isNormal == "NO_PARAM":
return True
def data2csv_batch_proc(filedir):
"""
data转csv批处理函数
:param filedir: 读取文件路径
:return:
"""
filelist = os.listdir(filedir)
print(filelist)
# file:文件名
for file in filelist:
# 设置输入文件地址
print(file)
input_file = input_file_dir + file
# 设置输出文件名
output_file_name = file[:22] + '.csv'
# 设置输出文件地址
output_file = output_file_dir + output_file_name
dat2csv(input_file, output_file)
data2csv_batch_proc(input_file_dir)
输入的dat文件示例截图:(已将敏感信息抹除,仅展示极小部分初始待机数据)
输出的csv文件示例截图:(仅展示极小部分,未展示敏感信息)
博主代码能力较弱,字符代号有些混乱,请稍见谅。效果不佳,仅作方案记录。