@@ -2487,28 +2487,16 @@ def csv2ann(file_name, extension='atr', fs=None, record_only=False,
24872487 if df_CSV .shape [1 ] == 2 :
24882488 if verbose :
24892489 print ('onset,description format detected' )
2490+ if not header :
2491+ df_CSV .columns = ['onset' , 'description' ]
24902492 df_out = df_CSV
24912493 elif df_CSV .shape [1 ] == 3 :
24922494 if verbose :
24932495 print ('onset,duration,description format detected' )
24942496 print ('Converting durations to single time-point events' )
2495- # Create two separate dataframes for the start and end annotation
2496- # then remove them from the original
2497- df_start = df_CSV [df_CSV ['duration' ] > 0 ]
2498- df_end = df_CSV [df_CSV ['duration' ] > 0 ]
2499- df_trunc = df_CSV [df_CSV ['duration' ] == 0 ]
2500- # Append parentheses at the start for annotation start and end for
2501- # annotation end
2502- df_start ['description' ] = '(' + df_start ['description' ].astype (str )
2503- df_end ['description' ] = df_end ['description' ].astype (str ) + ')'
2504- # Add the duration time to the onset for the end annotation to convert
2505- # to single time annotations only
2506- df_end ['onset' ] = df_end ['onset' ] + df_end ['duration' ]
2507- # Concatenate all of the dataframes
2508- df_out = pd .concat ([df_trunc , df_start , df_end ], ignore_index = True )
2509- # Make sure the sorting is correct
2510- df_out ['col_index' ] = df_out .index
2511- df_out = df_out .sort_values (['onset' , 'col_index' ])
2497+ if not header :
2498+ df_CSV .columns = ['onset' , 'duration' , 'description' ]
2499+ df_out = _format_ann_from_df (df_CSV )
25122500 else :
25132501 raise Exception ("""The number of columns in the CSV was not
25142502 recognized.""" )
@@ -2656,6 +2644,7 @@ def rdedfann(record_name, pn_dir=None, delete_file=True, info_only=True,
26562644 annotation_string = annotation_string .replace (rep ,' ' )
26572645
26582646 # Parse the resulting annotation string
2647+ onsets = []
26592648 onset_times = []
26602649 sample_nums = []
26612650 comments = []
@@ -2675,6 +2664,7 @@ def rdedfann(record_name, pn_dir=None, delete_file=True, info_only=True,
26752664 comment = ' ' .join (ann_split [2 :])
26762665 if verbose :
26772666 print (f'{ onset_time } \t { sample_num } \t { comment } \t \t duration: { duration } ' )
2667+ onsets .append (onset )
26782668 onset_times .append (onset_time )
26792669 sample_nums .append (sample_num )
26802670 comments .append (comment )
@@ -2689,12 +2679,70 @@ def rdedfann(record_name, pn_dir=None, delete_file=True, info_only=True,
26892679 'comment' : comments ,
26902680 'duration' : durations
26912681 }
2692- elif record_only :
2693- # TODO: return WFDB-formatted annotation object
2694- pass
26952682 else :
2696- # TODO: Create the WFDB annotation file and don't return the object
2697- pass
2683+ df_in = pd .DataFrame (data = {
2684+ 'onset' : onsets , 'duration' : durations , 'description' : comments
2685+ })
2686+ df_out = _format_ann_from_df (df_in )
2687+ # Remove extension from input file name
2688+ record_name = record_name .split (os .sep )[- 1 ].split ('.' )[0 ]
2689+ extension = 'atr'
2690+ fs = rec .fs
2691+ sample = (df_out ['onset' ].to_numpy ()* fs ).astype (np .int64 )
2692+ # Assume each annotation is a comment
2693+ symbol = ['"' ]* len (df_out .index )
2694+ subtype = np .array ([22 ]* len (df_out .index ))
2695+ # Assume each annotation belongs with the 1st channel
2696+ chan = np .array ([0 ]* len (df_out .index ))
2697+ num = np .array ([0 ]* len (df_out .index ))
2698+ aux_note = df_out ['description' ].tolist ()
2699+
2700+ if record_only :
2701+ return Annotation (record_name = record_name , extension = extension ,
2702+ sample = sample , symbol = symbol , subtype = subtype ,
2703+ chan = chan , num = num , aux_note = aux_note , fs = fs )
2704+ else :
2705+ wrann (record_name , extension , sample = sample , symbol = symbol ,
2706+ subtype = subtype , chan = chan , num = num , aux_note = aux_note ,
2707+ fs = fs )
2708+
2709+
2710+ def _format_ann_from_df (df_in ):
2711+ """
2712+ Parameters
2713+ ----------
2714+ df_in : Pandas dataframe
2715+ Contains all the information needed to create WFDB-formatted
2716+ annotations. Of the form:
2717+ onset,duration,description
2718+ onset_1,duration_1,description_1
2719+ onset_2,duration_2,description_2
2720+ ...,...,...
2721+
2722+ Returns
2723+ -------
2724+ N/A : Pandas dataframe
2725+ The WFDB-formatted input dataframe.
2726+
2727+ """
2728+ # Create two separate dataframes for the start and end annotation
2729+ # then remove them from the original
2730+ df_start = df_in [df_in ['duration' ] > 0 ]
2731+ df_end = df_in [df_in ['duration' ] > 0 ]
2732+ df_trunc = df_in [df_in ['duration' ] == 0 ]
2733+ # Append parentheses at the start for annotation start and end for
2734+ # annotation end
2735+ df_start ['description' ] = '(' + df_start ['description' ].astype (str )
2736+ df_end ['description' ] = df_end ['description' ].astype (str ) + ')'
2737+ # Add the duration time to the onset for the end annotation to convert
2738+ # to single time annotations only
2739+ df_end ['onset' ] = df_end ['onset' ] + df_end ['duration' ]
2740+ # Concatenate all of the dataframes
2741+ df_out = pd .concat ([df_trunc , df_start , df_end ], ignore_index = True )
2742+ # Make sure the sorting is correct
2743+ df_out ['col_index' ] = df_out .index
2744+ return df_out .sort_values (['onset' , 'col_index' ])
2745+
26982746
26992747
27002748## ------------- Annotation Field Specifications ------------- ##
0 commit comments