From 7d76945d0ecc4da50831fd84945a3398e916e153 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 31 Jul 2023 14:03:16 -0400 Subject: [PATCH] [FIX] handle nan and and datetimes when printing tables (#605) * fix bugs in tables and datetimes * skip on windows --- +bids/+util/tsvwrite.m | 27 ++++++++++++++++------ tests/tests_utils/test_tsvwrite.m | 37 ++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/+bids/+util/tsvwrite.m b/+bids/+util/tsvwrite.m index 57124f8b..494a12c4 100644 --- a/+bids/+util/tsvwrite.m +++ b/+bids/+util/tsvwrite.m @@ -7,16 +7,16 @@ function tsvwrite(filename, var) % tsvwrite(f, var) % % :param filename: - % :type filename: string + % :type filename: string + % % :param var: - % :type var: data array or structure + % :type var: data array or structure % % % Based on spm_save.m from SPM12. % - % + % (C) Copyright 2018 Guillaume Flandin, Wellcome Centre for Human Neuroimaging - % % (C) Copyright 2018 BIDS-MATLAB developers delim = sprintf('\t'); @@ -55,6 +55,7 @@ function tsvwrite(filename, var) var = num2cell(var); end + var = convert_datetimes(var); var = cellfun(@(x) num2str(x, 16), var, 'UniformOutput', false); var = strtrim(var); var(cellfun(@(x) strcmp(x, 'NaN'), var)) = {'n/a'}; @@ -64,9 +65,10 @@ function tsvwrite(filename, var) write_to_file(filename, var, delim); elseif isa(var, 'table') - writetable(var, filename, ... - 'FileType', 'text', ... - 'Delimiter', delim); + header = var.Properties.VariableNames; + var = table2cell(var); + var = cat(1, header, var); + bids.util.tsvwrite(filename, var); else error('Unknown data type.'); @@ -75,6 +77,17 @@ function tsvwrite(filename, var) end +function var = convert_datetimes(var) + if bids.internal.is_octave + return + end + is_datetime = find(cellfun(@(x) isdatetime(x), var(:))); + for i = 1:numel(is_datetime) + idx = is_datetime(i); + var{idx} = char(var{idx}, 'yyyy-MM-dd''T''HH:mm:ss.SSS'); % bids-compliant datetime + end +end + function write_to_file(filename, var, delim) fid = fopen(filename, 'Wt'); diff --git a/tests/tests_utils/test_tsvwrite.m b/tests/tests_utils/test_tsvwrite.m index 0d4ef5ce..587f6976 100644 --- a/tests/tests_utils/test_tsvwrite.m +++ b/tests/tests_utils/test_tsvwrite.m @@ -6,6 +6,41 @@ initTestSuite; end +function test_datetime_in_table() + % TODO fix line issue on windows + if bids.internal.is_octave() || ispc() + moxunit_throw_test_skipped_exception('no datetime in Octave and line issue in windows'); + end + x = '201401010000'; + acq_time = datetime(x, 'InputFormat', 'yyyyMMddHHmm'); + sub_id = {'foo'}; + patients = table(sub_id, acq_time); + file = fullfile(tempname(), 'foo.tsv'); + bids.util.mkdir(fileparts(file)); + bids.util.tsvwrite(file, patients); + + FID = fopen(file, 'r'); + C = textscan(FID, '%s%s', 'Delimiter', '\t', 'EndOfLine', '\n'); + assertEqual(C{2}{2}, '2014-01-01T00:00:00.000'); +end + +function test_nan_in_table() + % TODO fix line issue on windows + if bids.internal.is_octave() || ispc() + moxunit_throw_test_skipped_exception('no table in Octave and line issue in windows'); + end + sub_id = {'foo'; 'bar'}; + age = [25; nan]; + patients = table(sub_id, age); + file = fullfile(tempname(), 'foo.tsv'); + bids.util.mkdir(fileparts(file)); + bids.util.tsvwrite(file, patients); + + FID = fopen(file, 'r'); + C = textscan(FID, '%s%s', 'Delimiter', '\t', 'EndOfLine', '\n'); + assertEqual(C{2}{3}, 'n/a'); +end + function test_tsvwrite_basic() pth = fileparts(mfilename('fullpath')); @@ -67,7 +102,7 @@ function test_tsvwrite_basic() FID = fopen(tsv_file, 'r'); C = textscan(FID, '%s%s', 'Delimiter', '\t', 'EndOfLine', '\n'); - assertEqual(C{1}{2}, 'n/a'); % + assertEqual(C{1}{2}, 'n/a'); delete(tsv_file);