【问题标题】:Pandas read_csv low_memory and dtype optionsPandas read_csv low_memory 和 dtype 选项
【发布时间】:2014-08-06 17:20:25
【问题描述】:

打电话时

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

我明白了:

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

为什么dtype 选项与low_memory 相关,为什么将其设置为False 有助于解决这个问题?

【问题讨论】:

  • 我对此警告有疑问。提到的列的索引是从 0 开始的吗?例如具有混合类型的第 4 列是 df[:,4] 或 df[:,3]
  • @maziar 在读取 csv 时,默认创建和使用一个新的基于 0 的索引。
  • 这个答案对我很有效 stackoverflow.com/a/48187106/12079996>

标签: python parsing numpy pandas dataframe


【解决方案1】:

不推荐使用的 low_memory 选项

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

您收到此low_memory 警告的原因是因为猜测每一列的 dtypes 对内存的要求很高。 Pandas 尝试通过分析每列中的数据来确定要设置的 dtype。

Dtype 猜测(非常糟糕)

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

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

指定数据类型(应该总是这样做)

添加

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

存在哪些数据类型?

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

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

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

'category' 本质上是一个枚举(用整数键表示的字符串来保存

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

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

'Interval' 是一个独立的主题,但它的主要用途是索引。 See more here

'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' 时 pandas 爆炸。我想补充一点,在 pandas 中使用转换器确实很重且效率低下,应该作为最后的手段使用。这是因为 read_csv 进程是单个进程。

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

【讨论】:

  • 那么,鉴于设置dtype=object 并没有提高内存效率,除了摆脱错误之外,还有什么理由去搞砸它吗?
  • @zthomas.nc 是的,Pandas 不需要费心测试列中的内容。理论上在加载时节省一些内存(但在加载完成后没有),理论上节省一些 cpu 周期(你不会注意到,因为磁盘 I/O 将成为瓶颈。
  • "另外值得注意的是,如果文件的最后一行在 user_id 列中写入“foobar”,如果指定了上述 dtype,加载将崩溃。"是否有一些“强制”选项可以用来丢弃这一行而不是崩溃?
  • @sparrow 可能有,但我上次使用它时有错误。它可能在最新版本的 pandas 中得到修复。 error_bad_lines=False, warn_bad_lines=True 应该可以解决问题。文档说它只对 C 解析器有效。它还说默认解析器是 None 这使得很难知道哪个是默认解析器。
  • @nealmcb 您可以使用nrows=100 作为参数读取数据帧,然后执行df.dtypes 以查看您获得的数据类型。但是,在使用这些 dtype 读取整个数据帧时,请务必执行try/except,以便您发现错误的 dtype 猜测。你知道数据很脏。
【解决方案2】:

试试:

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

根据熊猫文档:

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

至于 low_memory,它是 True by default 并且尚未记录。我不认为它相关。错误消息是通用的,因此无论如何您都不需要弄乱low_memory。希望这会有所帮助,如果您还有其他问题,请告诉我

【讨论】:

  • 添加dtype=unicode 产生:NameError: name 'unicode' is not defined。但是将unicode 放在引号中(如'unicode')似乎有效!
  • @sedeh 您可以将 dtypes 指定为 python 类型或 numpy.dtype('unicode')。当你给 dtype 选项一个字符串时,默认情况下它会尝试通过numpy.dtype() 工厂进行转换。指定'unicode' 实际上不会做任何事情,unicode 只是向上转换为objects。你会得到dtype='object'
【解决方案3】:
df = pd.read_csv('somefile.csv', low_memory=False)

这应该可以解决问题。从 CSV 读取 180 万行时,我遇到了完全相同的错误。

【讨论】:

  • 这会消除错误,但实际上不会改变任何其他内容。
  • 我在运行 1.5gb 数据文件时遇到同样的问题
  • 我尝试时显示此错误,C 错误:内存不足
  • low_memory = False 到底在做什么?是解决问题还是不显示错误消息?
  • @JSVJ 我认为设置 low_memory = False 现在可以解决问题(请参阅我的答案)。似乎有一段时间它会被弃用,但这并没有发生。
【解决方案4】:

正如 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})

【讨论】:

    【解决方案5】:

    在处理一个巨大的 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'] = ''
    

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 这个答案与below 的答案相同,只是消除了错误,但没有改变 firelynx 指出的任何其他内容
      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        根据pandas documentation,指定low_memory=False,只要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 !!!!!
        
        

        旁白:举个例子,这是一个问题(我第一次遇到这个问题是一个严重的问题),假设您在一个文件上运行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
        

        【讨论】:

          【解决方案9】:

          这对我有用! file = pd.read_csv('example.csv', engine='python')

          【讨论】:

          • 也在这里,1+ 百万行,感谢
          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

            有时,当一切都失败时,你只想告诉熊猫闭嘴:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-05-14
              • 2019-11-30
              • 2016-10-17
              • 2013-05-31
              • 2015-02-01
              • 2020-07-26
              • 1970-01-01
              相关资源
              最近更新 更多