将缺少值的整数导出到Pandas中的csv

将缺少值的整数导出到Pandas中的csv,csv,pandas,int,nan,missing-data,Csv,Pandas,Int,Nan,Missing Data,将数据帧保存到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

将数据帧保存到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

编辑:我完全知道。问题是什么是一个很好的解决方法(特别是如果有许多其他不同类型的列,我事先不知道哪些“整数”列缺少值)。

@EdChum的建议是注释很好,您还可以使用
float\u format
参数(请参见中的)

发出:

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

我在这里扩展示例数据,希望能确保这是在处理您正在处理的情况:

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

这段代码可以实现您想要的功能,并且应该相对高效

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)
这将生成以下
csv
文件:

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

在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

为什么这是一个问题,没有办法表示int的
NaN
,因此转换为float。您必须将
NaN
值替换为可以表示为int(如
0
)的值,或者转换为字符串并将
NaN
字符串替换为空值,然后export@EdChum我知道
NaN
是浮点数。只是令人恼火的是,没有“缺少int”(从数据的角度来看,缺少的字段就是缺少的字段;缺少float没有什么特别之处)。问题是我不想将缺少的int导出为
0
,而是作为一个空字段(对于某些应用程序,我确实会将缺少的int转换为
-1
,但对于其他应用程序,这可能会有问题)。@PiotrMigdal我认为在这种情况下,您唯一的办法是转换为字符串并用空字符串填充nan,如前所述,我想我了解你在寻找什么,所以我尝试了一个答案。但是,您可能需要考虑扩展示例数据以更好地适应您的情况。我理解Korem/EdChum现有答案的局限性,但它确实产生了您要求b/c的结果。您的示例数据非常简单。似乎分类类型可能是可能解决方案的一部分,因为(理论上)您可以有一个包含NaN的整数映射。然而,如果我尝试这种转换,它最终会得到一个浮动索引,这否定了在这种情况下的任何实际改进,但它似乎有一些潜力。我不想将“普通浮点”格式化为
'%.0f'
。我只想将
int
(与
np.nan
s混合,它们是可悲的浮动)格式化为
'%.0f'
。谢谢!有道理;尽管如此,它仍然将恰好具有整数值的浮点强制为int(考虑一个值为
[1.0,-5.0,3.0]
的列)。然而,我看到的是,添加一个
np.nan
会更改所有条目的类型,因此不可能恢复原始条目。:/在这种情况下,我很好奇是否可以避免列强制转换(例如,具有
对象
类型和混合类型的元素)。编辑:似乎在创建
DataFrame
low\u memory=False
时,设置
dtype='object'
可以起作用。@PiotrMigdal刚刚编辑过,看一看。问题的第一部分应该通过添加
(如果有的话)(df[col].isnull()):
(还添加了一个新列)来解决。我不明白问题的第二部分。只有对象可以混合,但将数字存储为对象应该是最后的选择(我想在这种情况下),因为对象的数值性能比int/floats.Works差得多,但是
.fillna(0)
的作用是什么?这似乎是多余的。这是因为如果列中有
nan
s,则转换为
int64
不起作用。(虽然如果没有它也能用,也许我会把它拿出来…)
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