Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用多个分隔符高效地将数据从CSV读入数据帧_Python_Pandas_Performance_Csv_Dataframe - Fatal编程技术网

Python 使用多个分隔符高效地将数据从CSV读入数据帧

Python 使用多个分隔符高效地将数据从CSV读入数据帧,python,pandas,performance,csv,dataframe,Python,Pandas,Performance,Csv,Dataframe,我有一个笨拙的CSV文件,它有多个分隔符:非数字部分的分隔符是“”,“,数字部分的分隔符是“”;”。我希望尽可能高效地仅使用数字部分构造数据帧 我做了5次尝试:其中,使用了pd.read\u csv的converters参数,使用regex和engine='python',使用str.replace。它们都比不进行转换的情况下读取整个CSV文件慢2倍多。这对于我的用例来说太慢了 我理解这种比较不是一对一的比较,但它确实证明了总体性能差不是由I/O驱动的。有没有更有效的方法将数据读入数字数据帧?还

我有一个笨拙的CSV文件,它有多个分隔符:非数字部分的分隔符是“
”,“
,数字部分的分隔符是“
”;”。我希望尽可能高效地仅使用数字部分构造数据帧

我做了5次尝试:其中,使用了
pd.read\u csv
converters
参数,使用regex和
engine='python'
,使用
str.replace
。它们都比不进行转换的情况下读取整个CSV文件慢2倍多。这对于我的用例来说太慢了

我理解这种比较不是一对一的比较,但它确实证明了总体性能差不是由I/O驱动的。有没有更有效的方法将数据读入数字数据帧?还是等效的NumPy阵列

以下字符串可用于基准测试目的

# Python 3.7.0, Pandas 0.23.4

from io import StringIO
import pandas as pd
import csv

# strings in first 3 columns are of arbitrary length
x = '''ABCD,EFGH,IJKL,34.23;562.45;213.5432
MNOP,QRST,UVWX,56.23;63.45;625.234
'''*10**6

def csv_reader_1(x):
    df = pd.read_csv(x, usecols=[3], header=None, delimiter=',',
                     converters={3: lambda x: x.split(';')})
    return df.join(pd.DataFrame(df.pop(3).values.tolist(), dtype=float))

def csv_reader_2(x):
    df = pd.read_csv(x, header=None, delimiter=';',
                     converters={0: lambda x: x.rsplit(',')[-1]}, dtype=float)
    return df.astype(float)

def csv_reader_3(x):
    return pd.read_csv(x, usecols=[3, 4, 5], header=None, sep=',|;', engine='python')

def csv_reader_4(x):
    with x as fin:
        reader = csv.reader(fin, delimiter=',')
        L = [i[-1].split(';') for i in reader]
        return pd.DataFrame(L, dtype=float)

def csv_reader_5(x):
    with x as fin:
        return pd.read_csv(StringIO(fin.getvalue().replace(';',',')),
                           sep=',', header=None, usecols=[3, 4, 5])
检查:

res1 = csv_reader_1(StringIO(x))
res2 = csv_reader_2(StringIO(x))
res3 = csv_reader_3(StringIO(x))
res4 = csv_reader_4(StringIO(x))
res5 = csv_reader_5(StringIO(x))

print(res1.head(3))
#        0       1         2
# 0  34.23  562.45  213.5432
# 1  56.23   63.45  625.2340
# 2  34.23  562.45  213.5432

assert all(np.array_equal(res1.values, i.values) for i in (res2, res3, res4, res5))
基准测试结果:

%timeit csv_reader_1(StringIO(x))  # 5.31 s per loop
%timeit csv_reader_2(StringIO(x))  # 6.69 s per loop
%timeit csv_reader_3(StringIO(x))  # 18.6 s per loop
%timeit csv_reader_4(StringIO(x))  # 5.68 s per loop
%timeit csv_reader_5(StringIO(x))  # 7.01 s per loop
%timeit pd.read_csv(StringIO(x))   # 1.65 s per loop
更新
我愿意使用命令行工具作为最后手段。在这方面,我已经包括了这样一个答案。我希望有一个效率相当的纯Python或Pandas解决方案。

如果这是一个选项,请替换字符
,字符串中的
更快。
我已将字符串
x
写入文件
test.dat

def csv_reader_4(x):
    with open(x, 'r') as f:
        a = f.read()
    return pd.read_csv(StringIO(unicode(a.replace(';', ','))), usecols=[3, 4, 5])
unicode()
函数是避免Python 2中出现类型错误所必需的

基准:

%timeit csv_reader_2('test.dat')  # 1.6 s per loop
%timeit csv_reader_4('test.dat')  # 1.2 s per loop
使用命令行工具 到目前为止,我找到的最有效的解决方案是使用专业的命令行工具将
“;”
替换为
“,”
,然后读入Pandas。Pandas或纯Python解决方案在效率方面并不接近

基本上,使用cpython或用C/C++编写的工具可能优于Python级操作。 例如,使用:


如何使用生成器进行替换,并将其与适当的装饰器相结合,以获得适合熊猫的类似文件的对象

import io
import pandas as pd

# strings in first 3 columns are of arbitrary length
x = '''ABCD,EFGH,IJKL,34.23;562.45;213.5432
MNOP,QRST,UVWX,56.23;63.45;625.234
'''*10**6

def iterstream(iterable, buffer_size=io.DEFAULT_BUFFER_SIZE):
    """
    http://stackoverflow.com/a/20260030/190597 (Mechanical snail)
    Lets you use an iterable (e.g. a generator) that yields bytestrings as a
    read-only input stream.

    The stream implements Python 3's newer I/O API (available in Python 2's io
    module).

    For efficiency, the stream is buffered.
    """
    class IterStream(io.RawIOBase):
        def __init__(self):
            self.leftover = None
        def readable(self):
            return True
        def readinto(self, b):
            try:
                l = len(b)  # We're supposed to return at most this much
                chunk = self.leftover or next(iterable)
                output, self.leftover = chunk[:l], chunk[l:]
                b[:len(output)] = output
                return len(output)
            except StopIteration:
                return 0    # indicate EOF
    return io.BufferedReader(IterStream(), buffer_size=buffer_size)

def replacementgenerator(haystack, needle, replace):
    for s in haystack:
        if s == needle:
            yield str.encode(replace);
        else:
            yield str.encode(s);

csv = pd.read_csv(iterstream(replacementgenerator(x, ";", ",")), usecols=[3, 4, 5])
请注意,我们通过str.encode将字符串(或其组成字符)转换为字节,因为熊猫需要这样做


这种方法在功能上与Daniele的答案完全相同,只是我们“动态”地替换值,因为它们是被请求的,而不是一次完成的。

一个非常非常非常快速的方法,
3.51
就是结果,只需将
csv\u reader\u 4
制作如下,它只是将
StringIO
转换为
str
,然后替换
进行编码,并使用
sep=','读取数据帧。

def csv_reader_4(x):
    with x as fin:
        reader = pd.read_csv(StringIO(fin.getvalue().replace(';',',')), sep=',',header=None)
    return reader
基准:

%timeit csv_reader_4(StringIO(x)) # 3.51 s per loop
在我的环境(Ubuntu 16.04、4GB RAM、Python 3.5.2)中,最快的方法是(原型1)
csv\u reader\u 5
(取自),它的运行速度仅比不进行转换的情况下读取整个csv文件慢25%。我改进了这种方法,实现了一个过滤器/包装器来替换
read()
方法中的字符:

class SingleCharReplacingFilter:

    def __init__(self, reader, oldchar, newchar):
        def proxy(obj, attr):
            a = getattr(obj, attr)
            if attr in ('read'):
                def f(*args):
                    return a(*args).replace(oldchar, newchar)
                return f
            else:
                return a

        for a in dir(reader):
            if not a.startswith("_") or a == '__iter__':
                setattr(self, a, proxy(reader, a))

def csv_reader_6(x):
    with x as fin:
        return pd.read_csv(SingleCharReplacingFilter(fin, ";", ","),
                            sep=',', header=None, usecols=[3, 4, 5])
与无转换读取整个CSV文件相比,结果是性能稍好一些:

In [3]: %timeit pd.read_csv(StringIO(x))
605 ms ± 3.24 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [4]: %timeit csv_reader_5(StringIO(x))
733 ms ± 3.49 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit csv_reader_6(StringIO(x))
568 ms ± 2.98 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


< P > 1我称它为原型,因为它假定输入流是<代码> StringIO < /C> >类型(因为它上面调用了代码> .GETValueAudioor)/< P> Python具有强大的数据处理功能,但不要期望使用Python性能。当需要性能时,C和C++是您的朋友。
python中的任何快速库都是用C/C++编写的。在python中使用C/C++代码非常容易,请看一下swig实用工具()。您可以编写一个C++类,它可以包含一些快速工具,当需要时,您将在Python代码中使用这些工具。p> 您是否考虑过对多个分隔符使用正则表达式?例如:。不确定它是否会更快。@克里斯,现在我有了(请参见编辑),带有
engine='python'
的正则表达式比没有转换器的
pd慢8倍。读取\u csv
。@jpp,如果您使用
engine=c
,文档建议c引擎更快,而python引擎目前功能更完整。@pygo,文档解释正则表达式仅适用于python引擎。不行。是什么阻止了你只是更换了所有的;例如,在CSV文件中并正常导入?这对我来说会导致
MemoryError
,大概是因为它需要有效地读取所有内容两次?一旦进入
a
,然后进入
pd.DataFrame
。我想
a.replace
会创建一个副本。不幸的是,如果不使用更复杂的工具(如
cython
),我看不到一个简单的方法来避免这种情况。更好的是,使用流而不是覆盖文件。顺便说一句,使用
子流程。check_call
而不是
os.system
,因为它检查退出代码。@ivan_pozdeev,您能否详细介绍如何使用流而不是覆盖文件?其他地方有这样的例子吗?您是否在一致的硬件/设置上测试了相对性能?我看到这是一个较慢的解决方案,我已经用基准测试更新了我的问题。@jpp呃,你的计时与我的不同,我在Windows上。@U9 Forward我通过在
read()
操作期间进行替换改进了你的方法:好主意,但这一个计时时间为
2min1s
!在Python3.7、Pandas 0.23.4上,我在
pd.read\u csv
行上得到
ValueError:Invalid file path或buffer object type:
。有什么想法吗?@jpp Pandas 0.23.4对要被视为文件的对象有一个额外的要求,就像它必须有一个
\uu iter\uu
方法一样。我更新了我的答案以反映这一点。很抱歉延迟。我对此计时,它比我设置的
csv\u reader\u 1
长1秒(对于
\u 1
为4.28秒,对于
\u 6
为5.28秒)。根据我的问题,我正在使用输入
x=“”…”*10**6
,Python 3.7.0,Pandas 0.23.4,Windows。我知道这将取决于平台/设置。
In [3]: %timeit pd.read_csv(StringIO(x))
605 ms ± 3.24 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [4]: %timeit csv_reader_5(StringIO(x))
733 ms ± 3.49 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit csv_reader_6(StringIO(x))
568 ms ± 2.98 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)