I am reading text files that contain data from observations. The format is not Fixed Width
or Delimited
, so I built a generator that gets Key
, Values
pairs and yields a dictionary when it has read a full observation record (~75 pairs in my case). The main loop builds a list from these dictionaries and then loads them into a DataFrame
.
This code, although working, is slow and I realize it may be not optimum to build a long list of dictionaries to finally load into a DataFrame
.
Side notes:
- I am not a developer and new to Python
- I choose to use
Dictionaries
because my data files may contain different number of observations (Key
,Value
pairs) for each Record, but generally, this number should be consistent within one data file - I do not control the text files format, I have to live with it...
- The text files format is vaguely resembling JSON with each Record ([ ] enclosed) consisting of some (
Key
,Value
) pairs and nested Sub-Records both indented by 2 spaces, (Key
,Value
) pairs within Sub-Records are indented by 4 spaces and [ ] enclosed. Key
andValues
are separated by[space]:[space]
Values
can span multiple line, in this case the line ends with 4 spaces and the following lines are indented by more than 4 spaces, but this can be broken if theValue
contains aLF
character.
Is there a more efficient way to load my Key
, Value
dictionaries into my DataFrame
?
import glob
import pandas as pd
raw_ext = '.raw'
raw_path = input('RAW Files Folder path: ')
OBREP = '=' * 15
SBREP = '[\n'
KVSEP = ' : '
VCONT = ' \n'
VENDS = ' \n'
def get_rec_dict(file):
recs = {}
f = False
for line in file:
if OBREP in line:
f = True
if KVSEP in line and not line.endswith(SBREP):
vlist = line.split(KVSEP)
k = vlist.pop(0).strip()
if line.endswith(VCONT):
for line in file:
vlist.append(line.strip())
if not line.endswith(VCONT):
break
if k == 'User_Header':
for line in file:
if line == VENDS:
break
else:
vlist.append(line.strip())
v = '\n'.join(val.strip() for val in vlist)
recs[k] = v
if f:
if recs:
yield recs
f = False
file_no = 0
raw_files = glob.glob('{0}*{1}'.format(raw_path, raw_ext))
rec_list = []
for raw in raw_files:
with open(raw, 'r', encoding='latin-1') as infile:
for rec_dict in get_rec_dict(infile):
rec_list.append(rec_dict)
file_no += 1
df = pd.DataFrame(rec_list)
if file_no > 0:
print('{} RAW files loaded.'.format(file_no))
else:
print('No file found.')
RAW file sample:
This sample contains 2 Observations sets:
Obs_Report_Result : [ # ===== (1) ===== Observer_Report : [ # =============== Version : "5.0" Exploitation_Mode : NORMAL Line_Report : [ Filter_Type : 8N MIN Aux_Nb_Trace : 1 Seis_Nb_Trace : 16674 Total_Nb_Trace : 16675 Nb_Of_Dead_Seis_Channels : 9 Nb_Of_Live_Seis_Channels : 16665 Dead_Seis_Channels : 586:333(2152-2154) 658:384(12979-12981) 662:306(13345-13347) Live_Seis_Channels : 574:216-415(1-600) 578:216-415(601-1200) 582:216-415(1201-1800) 586:216-332(1801-2151)334-415(2155-2400) 590:216-415(2401-3000) 594:216-415(3001-3600) 598:216-415(3601-4200) 602:216-415(4201-4800) 606:216-415(4801-5400) 610:216-415(5401-6000) 614:216-415(6001-6600) 618:216-415(6601-7200) 622:216-415(7201-7800) 626:216-415(7801-8400) 630:216-279(8401-8592)291-415(8593-8967) 634:216-280(8968-9162)292-415(9163-9534) 638:216-280(9535-9729)291-415(9730-10104) 642:216-280(10105-10299)291-415(10300-10674) 646:216-415(10675-11274) 650:216-415(11275-11874) 654:216-415(11875-12474) 658:216-383(12475-12978)385-415(12982-13074) 662:216-305(13075-13344)307-415(13348-13674) 666:216-415(13675-14274) 670:216-415(14275-14874) 674:216-415(14875-15474) 678:216-415(15475-16074) 682:216-415(16075-16674) SFL : 574 SFN : 216 Spread_Nb : 1090 Spread_Type : ABSOLUTE Acq_Error : ITB : FALSE ] Shot_Report : [ Swath_Name : TDG_South Swath_ID : -2147483648 Shot_Nb : 2448 Line_Name : 317.0 Point_Number : 362.0 Point_Index : 1 Acq_Length : 16500 # (msec) Sweep_Length : 0 # (ms) Pilot_Length : 0 # (ms) Record_Length : 16500 # (ms) Sample_Rate : 1000 Total_Nb_Sample : 16501 Type_Of_Source : EXPLO Source_Nb : 11 Tb_Window : 2500 Date : Sun Feb 17 18:12:04 2015 Julian_Day : 1 Cog_State : NO COG Cog_Easting : N/A Cog_Northing : N/A Cog_Elevation : 0.0 Cog_Deviation : 0.0 Uphole_Time : 0.00 # (msec) ] Noise_Report : [ Noise_Elim_type : NE OFF Thres_Hold_Var : N/A Hist_Editing_Type : N/A Hist_Range : N/A # (dB) Hist_Taper_Length : N/A # (power) Hist_Thres_Init_Val : N/A # (dB) Hist_Zeroing_Length : N/A # (msec) Low_Trace_Value : 0 # (dB) Low_Trace_Percent : 0 Noisy_Trace_Percent : N/A Low_Noisy_Verbose : Nb_Of_Window : 0 ] Process_Report : [ Type_Of_Process : IMPULSIVE Acq_Nb : 1 Correl_Pilot_Nb : 0 Auto_Cor_Peak_Time : 0 Dump_Stacking_Fold : 1 Max_Of_Max_Aux_Char : " -7.929688e+01" Max_Of_Max_Seis_Char : " 1.088968e+06" Max_Time_Value_Verbose : "" ] Record_Report : [ File_Nb : 12221 Type_Of_Dump : DUMP Type_Of_Test : N/A 3 Tape_Nb : 36 Tape_Label : "TD South" Record_Type : NORMAL Blocking_Mode : FALSE Device_Bypass : FALSE Tape_Error_Text : "" Tape_Time : "Sun Feb 17 18:13:03 2015 " File_Count : "17 File_Per_Tape : "2000" ] Comment : "N/A" User_Header : "*SGD-S SP#2448/SL#317.0/SN#362.0/SI#1/SEQ#11/STA:1/CTB:00.000/UH:000.0 ICIS #105. Hits: 6. Single Hit Rec: 2.0s. Total Rec Length: 16.5s. NMEA: 5717.5386,N,11201.3849,W,+00408.3,M,1,06,07.2,000.04,270.0 TB=02182013,011205.6520652 Hit=02182013,011206.2430453 HP= 63PSI Hit=02182013,011208.7418981 HP= 83PSI Hit=02182013,011211.2414192 HP= 64PSI Hit=02182013,011213.7418408 HP= 79PSI Hit=02182013,011216.2420402 HP= 90PSI Hit=02182013,011218.7414871 HP= 71PSI Acquisition Complete. " ] # ===== (2) ===== Observer_Report : [ # =============== Version : "5.0" Exploitation_Mode : NORMAL Line_Report : [ Filter_Type : 8N MIN Aux_Nb_Trace : 1 Seis_Nb_Trace : 16674 Total_Nb_Trace : 16675 Nb_Of_Dead_Seis_Channels : 9 Nb_Of_Live_Seis_Channels : 16665 Dead_Seis_Channels : 586:333(2152-2154) 658:384(12979-12981) 662:306(13345-13347) Live_Seis_Channels : 574:216-415(1-600) 578:216-415(601-1200) 582:216-415(1201-1800) 586:216-332(1801-2151)334-415(2155-2400) 590:216-415(2401-3000) 594:216-415(3001-3600) 598:216-415(3601-4200) 602:216-415(4201-4800) 606:216-415(4801-5400) 610:216-415(5401-6000) 614:216-415(6001-6600) 618:216-415(6601-7200) 622:216-415(7201-7800) 626:216-415(7801-8400) 630:216-279(8401-8592)291-415(8593-8967) 634:216-280(8968-9162)292-415(9163-9534) 638:216-280(9535-9729)291-415(9730-10104) 642:216-280(10105-10299)291-415(10300-10674) 646:216-415(10675-11274) 650:216-415(11275-11874) 654:216-415(11875-12474) 658:216-383(12475-12978)385-415(12982-13074) 662:216-305(13075-13344)307-415(13348-13674) 666:216-415(13675-14274) 670:216-415(14275-14874) 674:216-415(14875-15474) 678:216-415(15475-16074) 682:216-415(16075-16674) SFL : 574 SFN : 216 Spread_Nb : 1090 Spread_Type : ABSOLUTE Acq_Error : ITB : FALSE ] Shot_Report : [ Swath_Name : TD_South Swath_ID : -2147483648 Shot_Nb : 2448 Line_Name : 317.0 Point_Number : 362.0 Point_Index : 1 Acq_Length : 16500 # (msec) Sweep_Length : 0 # (ms) Pilot_Length : 0 # (ms) Record_Length : 16500 # (ms) Sample_Rate : 1000 Total_Nb_Sample : 16501 Type_Of_Source : EXPLO Source_Nb : 11 Tb_Window : 2500 Date : Sun Feb 17 18:12:04 2015 Julian_Day : 1 Cog_State : NO COG Cog_Easting : N/A Cog_Northing : N/A Cog_Elevation : 0.0 Cog_Deviation : 0.0 Uphole_Time : 0.00 # (msec) ] Noise_Report : [ Noise_Elim_type : NE OFF Thres_Hold_Var : N/A Hist_Editing_Type : N/A Hist_Range : N/A # (dB) Hist_Taper_Length : N/A # (power) Hist_Thres_Init_Val : N/A # (dB) Hist_Zeroing_Length : N/A # (msec) Low_Trace_Value : 0 # (dB) Low_Trace_Percent : 0 Noisy_Trace_Percent : N/A Low_Noisy_Verbose : Nb_Of_Window : 0 ] Process_Report : [ Type_Of_Process : IMPULSIVE Acq_Nb : 1 Correl_Pilot_Nb : 0 Auto_Cor_Peak_Time : 0 Dump_Stacking_Fold : 1 Max_Of_Max_Aux_Char : " -7.929688e+01" Max_Of_Max_Seis_Char : " 1.088968e+06" Max_Time_Value_Verbose : "" ] Record_Report : [ File_Nb : 12221 Type_Of_Dump : DUMP Type_Of_Test : N/A 3 Tape_Nb : 36 Tape_Label : "TDG South" Record_Type : NORMAL Blocking_Mode : FALSE Device_Bypass : FALSE Tape_Error_Text : "" Tape_Time : "Sun Feb 17 18:13:08 2015 " File_Count : "17 File_Per_Tape : "2000" ] Comment : "N/A" User_Header : "*SGD-S SP#2448/SL#317.0/SN#362.0/SI#1/SEQ#11/STA:1/CTB:00.000/UH:000.0 ICIS #105. Hits: 6. Single Hit Rec: 2.0s. Total Rec Length: 16.5s. NMEA: 5717.5386,N,11201.3849,W,+00408.3,M,1,06,07.2,000.04,270.0 TB=02182013,011205.6520652 Hit=02182013,011206.2430453 HP= 63PSI Hit=02182013,011208.7418981 HP= 83PSI Hit=02182013,011211.2414192 HP= 64PSI Hit=02182013,011213.7418408 HP= 79PSI Hit=02182013,011216.2420402 HP= 90PSI Hit=02182013,011218.7414871 HP= 71PSI Acquisition Complete. " ]
VENDS
after the closing"
ofUser_Header
) I only get 1 sample dictionary yielded. What happens is thatf = True
will execute when reaching the first# ================
, process nothing as there is no' : '
in this line and reachif f:
; then turnf
toFalse
without yielding anything. The first observation is then parsed and, upon reaching the second# ===============
is yielded. The second observation is parsed but not yielded since there is no# ===============
any more. \$\endgroup\$with open('<path_to_file>') as f: print(len(list(get_rec_dict(f))))
to print 2. I get 0 with the original sample and 1 after adding two spaces after the closing"
ofUser_Header
. \$\endgroup\$data.split('\n')
, does that make any difference for you? \$\endgroup\$'Line_Report': '['
or'Observer_Report': '['
that are getting into it due to the split removing the line ending that should be present forSBREP
,VCONT
orVENDS
. \$\endgroup\$