Pandas read_csv: low_memory 和 dtype 选项

df = pd.read_csv('somefile.csv')

出现了一个错误:

.../site-package/anda/io/parsers.py: 1130: DtypePolice: 列(4,5,7,16)具有混合类型。请指定 dtype 选项或设置 low _ memory = False。

为什么 dtype选项与 low_memory相关,为什么 low_memory=False可能有帮助?

585996 次浏览

试一试:

dashboard_df = pd.read_csv(p_file, sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

根据熊猫的文件:

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

至于low_memory,它是True 默认情况下,还没有文档。但我认为这无关紧要。错误消息是通用的,所以无论如何您都不需要处理low_memory。希望这对你有所帮助,如果你还有其他问题,请告诉我

已弃用的low_memory选项

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

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

Dtype猜测(非常糟糕)

Pandas只能在读取整个文件时确定一个列应该具有什么dtype。这意味着在读取整个文件之前实际上无法解析任何内容,除非在读取最后一个值时必须冒险更改该列的dtype。

考虑一个文件的例子,其中有一个列名为user_id。 它包含1000万行,其中user_id总是数字。 由于pandas不能知道它只是数字,它可能会保留它作为原始字符串,直到它读取整个文件

指定dtypes(应该总是这样做)

添加

dtype={'user_id': int}

pd.read_csv()调用将使pandas知道当它开始读取文件时,这只是整数。

另外值得注意的是,如果文件的最后一行在user_id列中写入了"foobar",那么如果指定了上述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": "string"})


ValueError: invalid literal for long() with base 10: 'foobar'

dtypes通常是一个numpy的东西,在这里阅读更多关于它们的信息: http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html < / p >

存在哪些dtypes ?

我们可以访问numpy dtypes: float, int, bool, timedelta64[ns]和datetime64[ns]。注意,numpy日期/时间dtypes是时区感知的。

Pandas用它自己的dtype扩展了这组dtype:

'datetime64[ns, <tz>]'这是一个时区感知时间戳。

'category',本质上是一个enum(由整数键表示的字符串保存

'period[]'不要与timedelta混淆,这些对象实际上被锚定到特定的时间段

'Sparse', 'Sparse[int]', 'Sparse[float]'是用于稀疏数据或'有很多洞的数据',而不是在数据框架中保存NaN或None,它省略了对象,节省空间。

'Interval'本身是一个主题,但它的主要用途是索引。点击这里查看更多信息

'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64'都是可空的pandas特定整数,与numpy变体不同。

'string'是用于处理字符串数据的特定dtype,并允许访问该系列的.str属性。

'boolean'类似numpy 'bool',但它也支持缺失数据。

在这里阅读完整的参考资料:

Pandas dtype reference

陷阱,警告,注释

设置dtype=object将关闭上述警告,但不会提高内存效率,只会提高进程效率。

设置dtype=unicode不会起任何作用,因为对于numpy, unicode表示为object

转换器的使用

@sparrow正确地指出了转换器的用法,以避免在指定为int的列中遇到'foobar'时熊猫爆炸。我想补充一点,转换器在熊猫身上使用真的很重,效率也很低,应该作为最后的手段。这是因为read_csv进程是一个单独的进程。

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

df = pd.read_csv('somefile.csv', low_memory=False)

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

正如fireynx前面提到的,如果显式指定了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})

我在一个~400MB的文件中遇到了类似的问题。设置low_memory=False对我有用。首先做一些简单的事情,我会检查你的数据帧是否比你的系统内存大,重新启动,在继续之前清理RAM。如果你仍然遇到错误,值得确保你的.csv文件是好的,在Excel中快速查看并确保没有明显的损坏。损坏的原始数据会造成严重破坏。

它为我工作与low_memory = False导入数据帧。这就是所有对我有效的改变:

df = pd.read_csv('export4_16.csv',low_memory=False)

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

  1. 文件包含奇怪字符(使用编码修复)
  2. 未指定数据类型(使用dtype属性修复)
  3. 使用上面的方法,我仍然面临一个问题,这与无法基于文件名定义的file_format有关(使用try ..除了. .)
    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'] = ''

根据熊猫的文档,只要engine='c'(这是默认值)是这个问题的合理解决方案,就指定low_memory=False

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

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

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

下面的代码演示了一个示例,其中设置了low_memory=True,并且包含混合类型的列。它建立在@ fireynx的答案基础上

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 !!!!!


旁白:为了给出一个问题的例子(也是我第一次遇到这个严重问题的地方),假设你对一个文件运行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"

因为它把东西分成很多块,有时标识符81287是数字,有时是字符串。当我试图基于此删除副本时,

81287 == "81287"
Out[98]: False
正如错误所示,你应该在使用read_csv()方法时指定数据类型。 所以你应该写

file = pd.read_csv('example.csv', dtype='unicode')

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

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

这对我很管用!

file = pd.read_csv('example.csv', engine='python')

根据Jerald Achaibar给出的答案,我们可以检测混合Dytpes警告,并且只在警告发生时使用较慢的python引擎:

import warnings


# Force mixed datatype warning to be a python error so we can catch it and reattempt the
# load using the slower python engine
warnings.simplefilter('error', pandas.errors.DtypeWarning)
try:
df = pandas.read_csv(path, sep=sep, encoding=encoding)
except pandas.errors.DtypeWarning:
df = pandas.read_csv(path, sep=sep, encoding=encoding, engine="python")

这对我很管用!

dashboard_df = pd.read_csv(p_file, sep=';', error_bad_lines=False, index_col=False, dtype='unicode')