从多个数据源创建DataFrame时,我们应该使用for循环还是列表理解?

时间:2018-11-05 14:36:11

标签: python pandas dataframe

此问题与Merging files with similar name convention to a dataframe中的@jpp:s答案以及将先前线程(Put csv-files in separate dataframes depending on filename)标记为重复的决定有关,因为该线程中的三个答案均无效(2 / 3)或差(1/3)。

不管答案是否有效,据说一个答案(我的答案)的质量很差,因为"using concat within a for loop is explicitly not recommended by the docs"


受批评的方法

dataframes = {}
for filename in filenames:
    _df = pd.read_csv(filename)
    key = filename[:3]
    try:
       dataframes[key] = pd.concat([dataframes[key], _df], ignore_index=True)
    except KeyError:
       dataframes[key] = _df

可接受的方法dd是字典,其中每个值是文件名列表,每个键是每个文件名的前三个字符):

dict_of_dfs
for k, v in dd.items():
    dict_of_dfs[k] = pd.concat([pd.read_csv(fn) for fn in v], ignore_index=True)

现在,我同意concat调用(已接受的方法)中的列表理解要比for循环更高效,在for循环中,在每个{{1}上都调用concat }。

但这是否意味着我们应该始终通过在DataFrame调用(或DataFrames)中使用列表理解来从多个数据源创建concat,并且使用for循环是如此较差,实际上是错误?可读性又如何呢?我个人(当然)认为我的批评方法更具可读性。


如果我们阅读DataFrame.append上的append文档,我们会发现for循环或列表理解都不是“推荐的生成DataFrame的方法”:

  

以下虽然不推荐使用这种方法来生成DataFrames ,但它显示了两种从多个数据源生成DataFrame的方法。

     

效率较低:

pandas
  

更高效:

>>> df = pd.DataFrame(columns=['A'])
>>> for i in range(5):
...     df = df.append({'A': i}, ignore_index=True)
>>> df

   A
0  0
1  1
2  2
3  3
4  4

所以。我的问题如下:

  1. 正在循环并在多个数据源上使用>>> pd.concat([pd.DataFrame([i], columns=['A']) for i in range(5)], ... ignore_index=True) A 0 0 1 1 2 2 3 3 4 4 来创建concat的一个或多个实例,因此较差表示错误

  2. 在这种情况下我们应该始终使用列表理解吗?

  3. 文档似乎不建议既不使用列表解析也不使用for循环,那么推荐的从多个数据源创建DataFrame的方式是什么?


非常感谢您的回答@piRSquared和@jpp。我仍然不认为for循环中DataFrame的绝对解雇是,直到列表理解是正确的错误 接受

给出以下测试数据:

concat

方法:

df = pd.DataFrame({'A': np.arange(0, 25000), 'B': np.arange(0, 25000)})

for i in range(0, 50):
    df.to_csv('{}.csv'.format(i))

时间:

def conc_inside_loop(filenames):
    df = None
    for filename in filenames:

        if df is None:
           df = pd.read_csv(filename)
           continue

        df = pd.concat([df, pd.read_csv(filename)], ignore_index=True)

    return df

def conc_list_comprehension(filenames):
    return pd.concat([pd.read_csv(filename) for filename in filenames], ignore_index=True)

显然列表理解更有效(我已经说过,我理解这一点)。但是差异并不大。 我不认为,鉴于我们在这里看到的差异,您可以将一种方法称为“错误”,以至于错误,而将另一种方法称为正确

如@piRSquared所述,最后一个问题太广泛了。但是第三种方法是在for循环外使用>> %timeit -n 10 conc_inside_loop(glob.glob('*.csv')) 460 ms ± 15.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) >> %timeit -n 10 conc_list_comprehension(glob.glob('*.csv')) 363 ms ± 32.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

concat

2 个答案:

答案 0 :(得分:5)

  
      
  1. 正在循环使用concat并在多个数据源上创建DataFrame的一个或多个实例,以使认为错误
  2.   

是的!熊猫很棒。但是您应该不惜一切代价避免不必要地生产Pandas对象。创建Pandas对象可能很昂贵,DataFrame比Series更昂贵,但这对于所有python来说可能是True。对于“批判”方法:在循环中,创建一个Pandas对象,该对象将在循环的下一次迭代中被覆盖。相反,您应该考虑如何收集数据,以便在收集结束时生成Pandas对象。

  
      
  1. 在这种情况下我们应该始终使用列表理解吗?
  2.   

不!就像我在上面说的那样,可以将其视为收集数据以准备构造Pandas对象。理解只是这样一种收集方式。

  
      
  1. 文档似乎不建议既不使用列表理解也不使用for循环,那么推荐的从多个数据源创建DataFrame的方式是什么?
  2.   

这太广泛了。可以为许多方法提供理由。只是不要在循环中使用concatappend。我几乎每次都会说错。

“每次”实际上并不是指“每次”。我要做的意思是,永远不要在循环之前的某个时刻创建数据帧,然后循环执行,并且在每次迭代时都遇到将某些内容附加到先前初始化的数据帧的麻烦。每次迭代都变得非常昂贵。在“已接受”答案的情况下:它将数据帧分配给字典关键字,然后将其保留。它不会一再被弄乱。

答案 1 :(得分:3)

请注意,{strong>不推荐循环使用pd.DataFrame.append / pd.concat@piRSquared's answer解释了为什么文档在此问题上也很明确。

原因在于NumPy数组的结构。您无法有效地附加/连接它们;这些操作需要复制数据。这些操作占用大量内存,并且通常效率很低。

因此,“接受”方法是两种弊端中的较小者,因为您执行的pd.concat调用数量相对较少,而输入列表中的每个文件名则执行一次调用通过“批评”方法。

仅连接一次

您可以连接所有个数据帧,然后执行GroupBy操作:

df = pd.concat([pd.read_csv(fn).assign(file=fn.split('_')[0]) for fn in v],
               ignore_index=True)

dict_of_dfs = dict(tuple(df.groupby('file')))

对不起,关键是减少串联操作的次数。