databasematlabcsvread-data

How read the exact number of decimal digits with readtable from a .csv file?


I want to read a .csv file containing numbers with many digits by using the function readtable. Then I need to filter some rows and export the table to a .txt file. I manage to perform this task but the exported file contains numbers with less digits with respect to numbers stored into orginal .csv file.

How can I keep the same number of decimal digits as in the original file?

Here an example code: "NEOs_asteroids.csv" attached to this question:

clear all; close all; clc;

%Data import from .CSV files
file_name_asteroids = 'NEOs_asteroids.csv';
%Asteroid data reading 
opts = delimitedTextImportOptions("NumVariables", 11);
% Specify range and delimiter
opts.DataLines = [2, Inf];
opts.Delimiter = ",";
% Specify column names and types
opts.VariableNames = ["pdes", "name", "epoch", "a", "e", "i", "om", "w", "ma", "q", "ad"];
opts.VariableTypes = ["string", "string", "double", "double", "double", "double", "double", "double", "double", "double", "double"];
% Specify file level properties
opts.ExtraColumnsRule = "ignore";
opts.EmptyLineRule = "read";
% Specify variable properties
opts = setvaropts(opts, ["pdes", "name"], "WhitespaceRule", "preserve");
opts = setvaropts(opts, ["pdes", "name"], "EmptyFieldRule", "auto");
% Import the data
Ast_data = readtable(file_name_asteroids,opts);

%%Data filtering
i_max = 5; % (deg) 
e_max = 0.1;
q_min = 0.9;  %(AU)
ad_max = 1.1; % (AU)
Ast_cond = Ast_data.i <= i_max & Ast_data.e <= e_max &...
                Ast_data.q >= q_min & Ast_data.ad <= ad_max;
Ast_data_filtered = Ast_data(Ast_cond,:);

%%Data export for Fortran calculations
% find empty cells
% emptyCells = cellfun(@isempty,Ast_data.name);
% Fill empty cells with a customized string
% Ast_data.name(emptyCells) = '(Empty cell)'

Output_file_name = 'NEOs_asteroids_filtered.txt';
writetable(Ast_data_filtered,Output_file_name,...
    "WriteVariableNames",true,"Encoding",'UTF-8',"Delimiter","comma");

Here the .csv file "NEOs_asteroids.csv" (I have only inserted a few lines because of the excessive number of them):

"pdes","name","epoch","a","e","i","om","w","ma"
433,Eros,2459600.5,1.458273100364966,.2227273342470533,10.82846064209305,304.2963534821116,178.8971619902451,246.9041236220357
719,Albert,2459600.5,2.63750535267235,.5469586853330971,11.57526970278196,183.8552591536149,156.2275765643465,278.1971857577304
887,Alinda,2459600.5,2.473158049128383,.5704858457447907,9.394142747629626,110.4288007693465,350.4932417946612,86.60720939790032
1036,Ganymed,2459600.5,2.665849369179047,.5331216208605787,26.6779034163866,215.5172093946143,132.4281002671606,140.6548420252212
1221,Amor,2459600.5,1.918731194899893,.4358425037702864,11.88326264445768,171.320253219285,26.64392069638918,261.0445055510584
1566,Icarus,2459600.5,1.078092896249167,.8269435773988872,22.80421609750081,87.96160578064956,31.43750761745641,.3302900481594904
1580,Betulia,2459600.5,2.197288160959855,.4871550690589618,52.07925829047207,62.2921305585828,159.5134530240992,16.62891169416756
1620,Geographos,2459600.5,1.245689288382424,.3353623815747189,13.33753650197932,337.1849253154281,276.9463790257625,300.4925456677647
1627,Ivar,2459600.5,1.863273429711433,.3967369932429542,8.451956515515922,133.1203083485335,167.8035837515731,129.0289785118674
1685,Toro,2459600.5,1.367465568905196,.435966790549019,9.382739662870495,274.2362441040706,127.2114688956593,300.0617970060148
1862,Apollo,2459600.5,1.470502475713431,.5600430467767759,6.353083928279622,35.56898244993719,286.0386669340027,60.20237527657879
1863,Antinous,2459600.5,2.25922922966352,.6059920366929147,18.39886422664638,346.4237098245764,268.0896471241496,244.0101430178307
1864,Daedalus,2459600.5,1.460979692497152,.614486601048712,22.21536436924144,6.613194566464952,325.6563726322316,196.1166696209867
1865,Cerberus,2459600.5,1.079986459127503,.4669721847798676,16.09858904525619,212.8963846822369,325.2653260753237,170.2073771841984
1866,Sisyphus,2459600.5,1.893353802479725,.5383695942642949,41.20637430558688,63.47259682927852,293.0824330786623,331.1476239661798
1915,Quetzalcoatl,2459600.5,2.543790754476287,.5706410666538692,20.40472660837776,162.9348721741076,347.8091351438147,41.39145147165023
1916,Boreas,2459600.5,2.272324177667199,.4498393843780145,12.88297395642586,340.5981620510102,335.9167764123868,352.2401366045371
1917,Cuyo,2459600.5,2.149811212264342,.5054666607934076,23.95922692432141,188.2869783183211,194.5115030152427,82.28439227062766
1943,Anteros,2459600.5,1.430366783173011,.2560374828122278,8.706034969532251,246.3110706020363,338.3801329232052,173.8814283992249


Solution

  • It is likely that you are running into a precision limitation of the floating point format used internally by MATLAB. MATLAB by default uses doubles to store pretty much all numbers. For an IEEE double you're only going to get about 15 decimal digits.

    If you're not planning on performing computations on these numbers an option is to read them in as strings:

    opts = detectImportOptions(filename);
    opts = setvartype(opts, 'your variable', 'string');  % or 'char'
    data = readtable(filename, opts);
    

    If you want to perform computations on these numbers things are somewhat more difficult. A "built-in" option is to use the variable-precision arithmetic in the Symbolic Math Toolbox, or roll you own implementation. I'd consider if you really need the precision first before going down either of these paths for more precision.