将丢失值的整数导出到Pandas中的csv

时间:2014-09-11 13:55:51

标签: csv pandas int nan missing-data

将Pandas DataFrame保存到csv时,某些整数会在浮点数中转换。 它发生在一列浮点数缺少值(np.nan)的地方。

有一种简单的方法可以避免它吗? (特别是以自动方式 - 我经常处理各种数据类型的许多列。)

例如

import pandas as pd
import numpy as np
df = pd.DataFrame([[1,2],[3,np.nan],[5,6]],
                  columns=["a","b"],
                  index=["i_1","i_2","i_3"])
df.to_csv("file.csv")

产量

,a,b
i_1,1,2.0
i_2,3,
i_3,5,6.0

我想得到的是

,a,b
i_1,1,2
i_2,3,
i_3,5,6

编辑:我完全了解Support for integer NA - Pandas Caveats and Gotchas。问题是什么是一个很好的解决方法(特别是如果有许多其他类型的各种类型,我事先不知道哪些"整数"列有缺失的值)。

4 个答案:

答案 0 :(得分:6)

在to_csv函数中使用float_format = '%.12g'为我解决了类似的问题。它保留合法浮点数的小数位数,最多12个有效数字,但是因为存在NaN的情况而强制浮动的内容会丢弃它们:

In [4]: df
Out[4]: 
     a    b
i_1  1    2.0
i_2  3    NaN
i_3  5.9  6.0

In [5]: df.to_csv('file.csv', float_format = '%.12g')

输出是:

   , a,  b
i_1, 1,  2
i_2, 3, 
i_3, 5.9, 6

答案 1 :(得分:5)

这段代码可以满足您的需要,而且应该相对有效。

import numpy as np
import pandas as pd

EPSILON = 1e-9

def _lost_precision(s):
    """
    The total amount of precision lost over Series `s`
    during conversion to int64 dtype
    """
    try:
        return (s - s.fillna(0).astype(np.int64)).sum()
    except ValueError:
        return np.nan

def _nansafe_integer_convert(s):
    """
    Convert Series `s` to an object type with `np.nan`
    represented as an empty string ""
    """
    if _lost_precision(s) < EPSILON:
        # Here's where the magic happens
        as_object = s.fillna(0).astype(np.int64).astype(np.object)
        as_object[s.isnull()] = ""
        return as_object
    else:
        return s


def nansafe_to_csv(df, *args, **kwargs):
    """
    Write `df` to a csv file, allowing for missing values
    in integer columns

    Uses `_lost_precision` to test whether a column can be
    converted to an integer data type without losing precision.
    Missing values in integer columns are represented as empty
    fields in the resulting csv.
    """
    df.apply(_nansafe_integer_convert).to_csv(*args, **kwargs)

我们可以使用一个简单的DataFrame测试它,它应涵盖所有基础:

In [75]: df = pd.DataFrame([[1,2, 3.1, "i"],[3,np.nan, 4.0, "j"],[5,6, 7.1, "k"]]
                  columns=["a","b", "c", "d"],
                  index=["i_1","i_2","i_3"])
In [76]: df
Out[76]: 
     a   b    c  d
i_1  1   2  3.1  i
i_2  3 NaN  4.0  j
i_3  5   6  7.1  k

In [77]: nansafe_to_csv(df, 'deleteme.csv', index=False)

生成以下csv文件:

a,b,c,d
1,2,3.1,i
3,,4.0,j
5,6,7.1,k

答案 2 :(得分:4)

我正在扩展此处的示例数据,希望确保这样可以处理您正在处理的情况:

df = pd.DataFrame([[1.1,2,9.9,44,1.0],
                   [3.3,np.nan,4.4,22,3.0],
                   [5.5,8,np.nan,66,4.0]],
                  columns=list('abcde'),
                  index=["i_1","i_2","i_3"])

       a   b    c   d  e
i_1  1.1   2  9.9  44  1
i_2  3.3 NaN  4.4  22  3
i_3  5.5   8  NaN  66  4

df.dtypes

a    float64
b    float64
c    float64
d      int64
e    float64

我认为如果你想要一个通用的解决方案,由于pandas不允许在int列中使用NaN,因此必须对其进行显式编码。我在这里做的是检查整数(因为我们无法真正检查类型,因为如果它们包含NaN,它们将被重新设置为浮动),如果它是一个整数值,则转换为字符串格式,并将'NAN'转换为''(空)。当然,这不是你想要存储整数的方式,除非是在输出之前的最后一步。

for col in df.columns:
    if any( df[col].isnull() ):
        tmp = df[col][ df[col].notnull() ]
        if all( tmp.astype(int).astype(float) == tmp.astype(float) ):
            df[col] = df[col].map('{:.0F}'.format).replace('NAN','')

df.to_csv('x.csv')

这是输出文件,如果你把它读回到pandas中它看起来像什么,虽然它的目的可能是将它读入其他数字包。

%more x.csv

,a,b,c,d,e
i_1,1.1,2,9.9,44,1.0
i_2,3.3,,4.4,22,3.0
i_3,5.5,8,,66,4.0

pd.read_csv('x.csv')

  Unnamed: 0    a   b    c   d  e
0        i_1  1.1   2  9.9  44  1
1        i_2  3.3 NaN  4.4  22  3
2        i_3  5.5   8  NaN  66  4

答案 3 :(得分:2)

@EdChum的建议是评论很好,你也可以使用float_format参数(参见docs

In [28]: a
Out[28]: 
   a   b
0  0   1
1  1 NaN
2  2   3
In [31]: a.to_csv(r'c:\x.csv', float_format = '%.0f')

退出:

,a,b
0,0,1
1,1,
2,2,3