【问题标题】:MATLAB conversion: CELL to DOUBLE; But numbers changeMATLAB 转换:CELL 到 DOUBLE;但是数字变了
【发布时间】:2016-12-06 11:50:55
【问题描述】:

我正在读取一个 excel 文件。在其中一列(仅包含很长的数字)中,MATLAB 将其作为 CELL 导入(因为有一个标题)。

这是导入的示例:
'980000684210053338'

这些是我的尝试:

转换为 DOUBLE 但数字发生变化
tableM.v1 = cellfun(@str2num,tableM.v1);

转换为 DOUBLE 但数字发生变化
tableM.v1 = cellfun(@str2double,tableM.v1);

转换为 CHAR 但数字正确
tableM.v1 = cell2mat(tableM.v1);

如何在保持正确值的同时将此 CELL 转换为 DOUBLE?

谢谢

附:我正在使用 MATLAB R2016a。

更新:
我从答案中运行了这段代码:
tableM.v1 = vpa(tableM.v1); % assuming tableM.v1 is a cellstr

我收到了这个错误:

警告:支持不是有效变量名的字符串或 定义一个数字将在未来的版本中删除。创造 符号表达式,首先创建符号变量,然后 对它们使用操作。
在 sym>convertExpression(第 1536 行)
在 sym>convertChar(第 1441 行)
在 sym>tomupad(第 1198 行)
在 sym(第 177 行)
在 cell2sym 中(第 28 行)
在 sym>tomupad(第 1208 行)
在 sym(第 177 行)
在 vpa 中(第 44 行)
使用 symengine 时出错
新数组必须具有与原始数组相同数量的元素 数组。

sym/reshape 错误(第 50 行)
ySym = mupadmex('symobj::reshape',x.s,args{:});

cell2sym 中的错误(第 34 行)
S = reshape(sym([Csym{:}]), size(C));

符号错误>tomupad(第 1208 行)
xsym = cell2sym(x);

符号错误(第 177 行)
S.s = tomupad(x);

vpa 中的错误(第 44 行)
ss = sym(s);

【问题讨论】:

    标签: excel matlab char double cell


    【解决方案1】:

    更好的答案

    下面的答案是公认的答案,但在拖延某些事情时,我意识到这太聪明了。我认为您真正想要的是使用textscan

    tableM.v1 = cellfun(@(x) textscan(x, '%u64'), tableM.v1);
    

    Textscan 已经检查了指数和小数,直接进入整数类而不通过双精度,并且正确溢出(我下面的溢出示例不太正确,因为指数向量也会溢出。最大值 uint64 实际上是 9223372036854775807 )。但是,您最终会得到一个数字元胞数组,而不是一个漂亮的向量,因为这就是textscan 吐出的内容。任何格式错误的数字都会导致空单元格,您必须在转换为向量之前处理这些问题。

    >> in = {'cat', '1e10', '980000684210053338};
    >> out = cellfun(@(x) textscan(x, '%u64'), in)
    out =
    
      1×3 cell array
    
      [0×1 uint64]    [10000000000]    [980000684210053338]
    

    修复这些问题后,您可以使用cell2mat 转换为矢量。


    原答案

    正如其他答案所指出的那样,由于精度损失,双打无法保持这些数字。您需要将它们转换为 64 位整数,而无需先通过 double 过滤器。试试这个三行函数:

    function out = str2uint64(in)
        % Convert the digits into an array of numbers and cast to
        % uint64
        in = uint64(in - 48);
    
        % Create the order of magnitude for each digit and convert
        % that also to uint64
        exponents = uint64(logspace(numel(in)-1, 0, numel(in)));
    
        % Why would sum default to convert your numbers to doubles?!?
        % The 'native' tag is recent, I believe, but if you have it,
        % it will preserve the data type.
        out = sum(in .* exponents, 'native');
    end
    

    使用方法:

    tableM.v1 = cellfun(@str2uint64,tableM.v1);
    

    需要注意的是,由于某些愚蠢的原因,当 MATLAB 对数字求和时,它会将它们转换为 double。在当前版本 R2016b 中,有一个标志告诉它在不强制转换的情况下求和。我不知道该标志何时发布,因此您的里程可能会有所不同。如果您没有该选项,则必须改为循环求和。

    另一个警告:这个函数没有输入或输出检查,所以str2uint64('cat') = 5658(我只是根据它们在ASCII表中的位置转换数字)和str2uint64('1000000000000000000') = 18446744073709551615(溢出)。使用风险自负。


    【讨论】:

    • 不错的 +1。看到 MATLAB 还没有 str2int* 套件,我感到非常惊讶。内部double 选角也非常奇怪和烦人。我注意到,由于某种原因,在将 VPA 非标量转换为 uint64 时也会发生这种情况:str = '980000684210053338'; disp(uint64(vpa(str)));disp(uint64(vpa(repmat({str},1,2)))); 会产生不同的结果,这很荒谬。
    • 这似乎可行,不过是一个小问题。它将“0,0090”转换为“90”​​或“3,5610”转换为“305610”。
    • 您的示例不包含数字以外的字符。这就是为什么我添加了关于错误检查的最后一个警告。该函数还假定您的输入已经是整数,而不是浮点数,因为您没有表明它是一个选项。这将需要一些额外的步骤来搜索小数指示符,然后截断数字。
    • 我知道。我只是用不同的数字检查它,看看有什么限制,所以我有点了解。我已经接受了你的回答。谢谢。
    • @PeyM87 我知道你已经接受了我的回答,但我想我想出了一个更好、更强大的解决方案。
    【解决方案2】:

    双精度数最多有15 stable decimal placesMathWorks puts it,“double 值不能正确表示大于 253 的所有整数”。由于 Excel 数字有 18 位有效小数位,因此不可避免地会丢失 doubleconversion 的精度。

    为避免精度损失,您可以将字符串转换为使用variable precision arithmetic 的数字:

     tableM.v1 = vpa(tableM.v1); % assuming tableM.v1 is a cellstr
    

    这很可能会降低性能,但这是精确表示的回报(直到 MATLAB 本身支持 128 位浮点,这可能在时间上很遥远,而且性能更密集)。

    理论上,uint64 也可以准确地保存整数,但似乎没有一种干净的方法可以将字符串转换为我能找到的 n 位整数。

    【讨论】:

    • 我在使用此代码时收到错误消息。我已经用错误更新了问题。
    • @PeyM87 这告诉我tableM.v1 不完全是一个只包含数字的字符串元胞数组。这是唯一可行的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多