Pandas read_csv low_memory和dtype选项

Jos*_*osh 270 python parsing numpy dataframe pandas

打电话的时候

df = pd.read_csv('somefile.csv')
Run Code Online (Sandbox Code Playgroud)

我明白了:

/Users/josh/anaconda/envs/py27/lib/python2.7/site-packages/pandas/io/parsers.py:1130:DtypeWarning:列(4,5,7,16)有混合类型.在导入时指定dtype选项或设置low_memory = False.

为什么该dtype选项与此相关low_memory,以及为什么会False帮助解决此问题?

fir*_*ynx 366

已弃用的low_memory选项

low_memory选项没有被正确弃用,但它应该是,因为它实际上没有做任何不同的事情[ 来源 ]

你得到这个low_memory警告的原因是因为猜测每列的dtypes是非常需要内存的.Pandas试图通过分析每列中的数据来确定要设置的dtype.

Dtype猜测(非常糟糕)

Pandas只能确定读取整个文件后列应该具有什么类型.这意味着在读取整个文件之前无法真正解析任何内容,除非您在读取最后一个值时可能需要更改该列的dtype.

考虑一个文件的示例,该文件具有名为user_id的列.它包含1000万行,其中user_id始终为数字.由于大熊猫不知道它只是数字,它可能会将它保留为原始字符串,直到它读取整个文件.

指定dtypes(应该总是这样)

加入

dtype={'user_id': int}
Run Code Online (Sandbox Code Playgroud)

pd.read_csv()呼叫将使大熊猫知道它开始读取文件时,认为这是唯一的整数.

另外值得注意的是,如果文件中的最后一行已"foobar"写入user_id列中,如果指定了上述dtype,则加载会崩溃.

定义dtypes时中断的数据损坏示例

import pandas as pd
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO


csvdata = """user_id,username
1,Alice
3,Bob
foobar,Caesar"""
sio = StringIO(csvdata)
pd.read_csv(sio, dtype={"user_id": int, "username": object})

ValueError: invalid literal for long() with base 10: 'foobar'
Run Code Online (Sandbox Code Playgroud)

dtypes通常是一个numpy事情,在这里阅读更多关于它们:http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html

什么dtypes存在?

这些是大熊猫也接受的numpy dtypes

[numpy.generic,
 [[numpy.number,
   [[numpy.integer,
     [[numpy.signedinteger,
       [numpy.int8,
        numpy.int16,
        numpy.int32,
        numpy.int64,
        numpy.int64,
        numpy.timedelta64]],
      [numpy.unsignedinteger,
       [numpy.uint8,
        numpy.uint16,
        numpy.uint32,
        numpy.uint64,
        numpy.uint64]]]],
    [numpy.inexact,
     [[numpy.floating,
       [numpy.float16, numpy.float32, numpy.float64, numpy.float128]],
      [numpy.complexfloating,
       [numpy.complex64, numpy.complex128, numpy.complex256]]]]]],
  [numpy.flexible,
   [[numpy.character, [numpy.bytes_, numpy.str_]],
    [numpy.void, [numpy.record]]]],
  numpy.bool_,
  numpy.datetime64,
  numpy.object_]]
Run Code Online (Sandbox Code Playgroud)

熊猫还添加了两个dtypes:categorical并且datetime64[ns, tz]在numpy中不可用

熊猫dtype参考

陷阱,警告,笔记

设置dtype=object将使上述警告静音,但不会使内存效率更高,只有处理有效.

设置dtype=unicode不会做任何事情,因为numpy,a unicode表示为object.

使用转换器

@sparrow正确地指出转换器的使用,以避免'foobar'在指定的列中遇到大熊猫时炸毁int.我想补充一点,转换器在pandas中使用非常繁重且效率低,应该作为最后的手段使用.这是因为read_csv进程是一个进程.

CSV文件可以逐行处理,因此可以通过简单地将文件切割成段并运行多个进程来更有效地并行处理多个转换器,这是熊猫不支持的.但这是一个不同的故事.

  • 因此,假设设置一个`dtype = object`并不是更高效的内存,除了摆脱错误之外,有没有理由搞乱它? (6认同)
  • @ zthomas.nc是的,Pandas不需要费心测试列中的内容.从理论上讲,在加载时节省了一些内存(但在加载完成后没有任何内存),理论上可以节省一些cpu周期(由于磁盘I/O将是你的瓶颈所以你不会注意到这一点. (5认同)
  • "另外值得注意的是,如果文件中的最后一行在user_id列中写有"foobar",如果指定了上面的dtype,加载就会崩溃." 是否有一些"强制"选项可以用来扔掉这一行而不是崩溃? (5认同)
  • @sparrow可能有,但上次我用它有bug.它可以在最新版本的熊猫中修复.`error_bad_lines = False,warn_bad_lines = True`应该可以解决问题.文档说它只对C解析器有效.它还说默认解析器是None,这使得很难知道哪一个是默认的. (5认同)
  • @nealmcb您可以使用`nrows = 100`作为参数读取数据帧,然后执行`df.dtypes`以查看您获得的dtypes.但是,当使用这些dtypes读取整个数据帧时,请务必执行`try/except`,以便捕获错误的dtype猜测.你知道数据很脏. (3认同)
  • 从理论上讲,我理解提前指定所有col_types肯定更好,但是当你有数千列时,是否有一些实用的解决方案?例如,我只想指定抛出混合类型警告的列的dtypes (2认同)

hd1*_*hd1 41

尝试:

dashboard_df = pd.read_csv(p_file, sep=',', error_bad_lines=False, index_col=False, dtype='unicode')
Run Code Online (Sandbox Code Playgroud)

根据熊猫文档:

dtype:列类型名称或字典 - >类型

至于low_memory,默认为True ,尚未记录.我不认为它的相关性.错误消息是通用的,所以无论如何你都不需要搞乱low_memory.希望这有帮助,如果您有其他问题,请告诉我

  • @sedeh您可以将dtypes指定为python类型或指定为`numpy.dtype('unicode')`.当你给dtype选项一个字符串时,默认情况下会尝试通过`numpy.dtype()`工厂强制转换它.指定''unicode''实际上什么都不做,unicodes只是升级为`objects`.你会得到`dtype ='object' (5认同)

小智 32

df = pd.read_csv('somefile.csv', low_memory=False)
Run Code Online (Sandbox Code Playgroud)

这应该可以解决问题.当从CSV读取1.8M行时,我得到了完全相同的错误.

  • 这会使错误变得沉默,但实际上并没有改变任何其他错误. (41认同)
  • 我在运行 1.5gb 数据文件时遇到同样的问题 (3认同)

小智 31

这对我有用!

file = pd.read_csv('example.csv', engine='python')
Run Code Online (Sandbox Code Playgroud)


spa*_*row 16

如前面提到的firelynx,如果明确指定了dtype并且存在与该dtype不兼容的混合数据,则加载将崩溃.我使用这样的转换器作为解决方法来更改具有不兼容数据类型的值,以便仍然可以加载数据.

def conv(val):
    if not val:
        return 0    
    try:
        return np.float64(val)
    except:        
        return np.float64(0)

df = pd.read_csv(csv_file,converters={'COL_A':conv,'COL_B':conv})
Run Code Online (Sandbox Code Playgroud)


小智 7

在处理巨大的 csv 文件(600 万行)时,我遇到了类似的问题。我遇到了三个问题:

  1. 文件包含奇怪的字符(使用编码修复)
  2. 未指定数据类型(使用 dtype 属性修复)
  3. 使用上面的内容我仍然遇到一个与无法根据文件名定义的 file_format 相关的问题(使用 try .. except .. 修复)
    df = pd.read_csv(csv_file,sep=';', encoding = 'ISO-8859-1',
                     names=['permission','owner_name','group_name','size','ctime','mtime','atime','filename','full_filename'],
                     dtype={'permission':str,'owner_name':str,'group_name':str,'size':str,'ctime':object,'mtime':object,'atime':object,'filename':str,'full_filename':str,'first_date':object,'last_date':object})
    
    try:
        df['file_format'] = [Path(f).suffix[1:] for f in df.filename.tolist()]
    except:
        df['file_format'] = ''
Run Code Online (Sandbox Code Playgroud)


tec*_*age 7

有时,当所有其他方法都失败时,你只想告诉熊猫闭嘴:

# Ignore DtypeWarnings from pandas' read_csv                                                                                                                                                                                            
warnings.filterwarnings('ignore', message="^Columns.*")
Run Code Online (Sandbox Code Playgroud)

  • 这并不能解决问题。它只是隐藏它 (2认同)
  • 取决于警告本身是否是问题。 (2认同)

小智 5

它在low_memory = False导入 DataFrame 时对我有用。这就是对我有用的所有变化:

df = pd.read_csv('export4_16.csv',low_memory=False)
Run Code Online (Sandbox Code Playgroud)


Ric*_*lvo 5

根据pandas 文档,指定low_memory=Falselong engine='c'(这是默认值)是解决此问题的合理解决方案。

如果low_memory=False,则将首先读取整列,然后确定正确的类型。例如,列将根据需要保留为对象(字符串)以保存信息。

如果low_memory=True(默认),则 pandas 以行块的形式读取数据,然后将它们附加在一起。然后,某些列可能看起来像是整数和字符串混合的块,具体取决于块中 pandas 是否遇到了无法转换为整数的任何内容(比如说)。这可能会在以后引起问题。该警告告诉您这种情况在读入过程中至少发生过一次,因此您应该小心。设置low_memory=False会使用更多内存,但会避免问题。

就我个人而言,我认为low_memory=True这是一个糟糕的默认设置,但我工作的领域使用的小数据集多于大数据集,因此便利性比效率更重要。

以下代码说明了一个示例,其中low_memory=True设置了 ,并且列具有混合类型。它建立在@firelynx 的答案之上

import pandas as pd
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

# make a big csv data file, following earlier approach by @firelynx
csvdata = """1,Alice
2,Bob
3,Caesar
"""

# we have to replicate the "integer column" user_id many many times to get
# pd.read_csv to actually chunk read. otherwise it just reads 
# the whole thing in one chunk, because it's faster, and we don't get any 
# "mixed dtype" issue. the 100000 below was chosen by experimentation.
csvdatafull = ""
for i in range(100000):
    csvdatafull = csvdatafull + csvdata
csvdatafull =  csvdatafull + "foobar,Cthlulu\n"
csvdatafull = "user_id,username\n" + csvdatafull

sio = StringIO(csvdatafull)
# the following line gives me the warning:
    # C:\Users\rdisa\anaconda3\lib\site-packages\IPython\core\interactiveshell.py:3072: DtypeWarning: Columns (0) have mixed types.Specify dtype option on import or set low_memory=False.
    # interactivity=interactivity, compiler=compiler, result=result)
# but it does not always give me the warning, so i guess the internal workings of read_csv depend on background factors
x = pd.read_csv(sio, low_memory=True) #, dtype={"user_id": int, "username": "string"})

x.dtypes
# this gives:
# Out[69]: 
# user_id     object
# username    object
# dtype: object

type(x['user_id'].iloc[0]) # int
type(x['user_id'].iloc[1]) # int
type(x['user_id'].iloc[2]) # int
type(x['user_id'].iloc[10000]) # int
type(x['user_id'].iloc[299999]) # str !!!! (even though it's a number! so this chunk must have been read in as strings)
type(x['user_id'].iloc[300000]) # str !!!!!

Run Code Online (Sandbox Code Playgroud)

旁白:举个例子,说明这是一个问题(也是我第一次遇到这个严重问题的地方),假设您运行了pd.read_csv()一个文件,然后想要根据标识符删除重复项。假设标识符有时是数字,有时是字符串。一行可能是“81287”,另一行可能是“97324-32”。尽管如此,它们仍然是唯一的标识符。

使用low_memory=True,pandas 可能会像这样读取标识符列:

81287
81287
81287
81287
81287
"81287"
"81287"
"81287"
"81287"
"97324-32"
"97324-32"
"97324-32"
"97324-32"
"97324-32"
Run Code Online (Sandbox Code Playgroud)

正因为它对事物进行分块,所以有时标识符 81287 是一个数字,有时是一个字符串。当我尝试基于此删除重复项时,嗯,

81287 == "81287"
Out[98]: False
Run Code Online (Sandbox Code Playgroud)


小智 5

正如错误所说,您应该在使用该read_csv()方法时指定数据类型。所以,你应该写

file = pd.read_csv('example.csv', dtype='unicode')
Run Code Online (Sandbox Code Playgroud)