将多个csv文件导入到pandas中并串联到一个DataFrame中


403

我想将目录中的多个csv文件读入pandas,并将它们连接成一个大的DataFrame。我还无法弄清楚。这是我到目前为止的内容:

import glob
import pandas as pd

# get data file names
path =r'C:\DRO\DCL_rawdata_files'
filenames = glob.glob(path + "/*.csv")

dfs = []
for filename in filenames:
    dfs.append(pd.read_csv(filename))

# Concatenate all data into one DataFrame
big_frame = pd.concat(dfs, ignore_index=True)

我想我在for循环中需要一些帮助吗???


你的代码不执行任何操作,因为你不是附加到你的dfs列表,你不希望替换行data = pd.read_csv(filename)dfs.append(pd.read_csv(filename)。然后,您需要遍历列表和concat,我认为concat不会在dfs 列表上工作。
EdChum 2014年

同样,您也将模块的别名与最后一行中的模块名称混合在一起,不是big_frame = pd.concat(dfs, ignore_index=True)吗?无论如何,一旦有了数据帧列表,您就需要遍历该列表并连接到big_frame
EdChum,2014年

是的,我编辑了代码,但仍然无法从csv文件构建级联的数据帧,我是python的新手,所以我需要更多帮助
jonas 2014年

您需要立即进行循环dfs,因此for df in dfs: big_frame.concat(df, ignore_index=True)应该可以进行类似的操作,也可以尝试append而不是concat也可以。
EdChum 2014年

您能更确切地说明什么不起作用吗?因为concat应该像您一样处理DataFrames列表。我认为这是一个非常好的方法。
joris 2014年

Answers:


454

如果所有csv文件中的列均相同,则可以尝试以下代码。我已添加,header=0以便在读取后csv可以将第一行分配为列名。

import pandas as pd
import glob

path = r'C:\DRO\DCL_rawdata_files' # use your path
all_files = glob.glob(path + "/*.csv")

li = []

for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0)
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True)

尤其是这似乎是一种老式的手动操作方式。由于Hapood生态系统的工具列表越来越多,您可以在包含不同文件类型(csv,json,txt,数据库)的许多不同目录中直接执行sql查询,就好像它是一个数据源一样。python中一定有类似的东西,因为它在进行“大数据”开发方面已有20年的飞跃。
Hexatonic

275
相同的事物更简洁,并且可能更快,因为它不使用列表: df = pd.concat((pd.read_csv(f) for f in all_files)) 此外,也许应该使用os.path.join(path, "*.csv")代替path + "/*.csv",这使它与操作系统无关。
Sid 2016年

4
使用此答案使我可以使用文件名添加新列,例如df['filename'] = os.path.basename(file_)for for file_ loop中的..不知道Sid的答案是否允许这样做?
curtisp

4
@curtisp您仍然可以使用Sid的答案进行操作,只需pandas.read_csv(f).assign(filename = foo)在生成器内部使用即可。 assign将返回整个数据帧,包括新列filename
C8H10N4O2 '17

如果您有很多文件,那么在串联所有文件之前,我会使用生成器,而不是导入+追加到列表中。
gustafbstrom

289

替代darindaCoder的答案

path = r'C:\DRO\DCL_rawdata_files'                     # use your path
all_files = glob.glob(os.path.join(path, "*.csv"))     # advisable to use os.path.join as this makes concatenation OS independent

df_from_each_file = (pd.read_csv(f) for f in all_files)
concatenated_df   = pd.concat(df_from_each_file, ignore_index=True)
# doesn't create a list, nor does it append to one

2
@Mike @Sid最后两行可以替换为:pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)。熊猫0.18.1版需要使用内部支架
Igor Fobia '16

6
我建议使用glob.iglob代替glob.glob; 第一个返回和迭代器(而不是列表)
toto_tico

54
import glob, os    
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))

4
出色的一种衬里,如果不需要read_csv参数,则特别有用!
rafaelvalle

15
另一方面,如果需要参数,则可以使用lambdas完成:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
fiedl

^或使用functools.partial,避免使用lambdas
cs95


30

这里几乎所有答案都是不必要的复杂(全局模式匹配)或依赖于其他第三方库。您可以使用已内置的Pandas和python(所有版本)在2行中执行此操作。

对于一些文件-1个衬纸:

df = pd.concat(map(pd.read_csv, ['data/d1.csv', 'data/d2.csv','data/d3.csv']))

对于许多文件:

from os import listdir

filepaths = [f for f in listdir("./data") if f.endswith('.csv')]
df = pd.concat(map(pd.read_csv, filepaths))

设置df的这条熊猫线利用了3件事:

  1. Python的地图(函数,可迭代)发送到函数( pd.read_csv()可迭代(我们的列表)(是文件路径中的每个csv元素)。
  2. 熊猫的read_csv()函数可以正常读取每个CSV文件。
  3. 熊猫的concat()将所有这些都放在一个df变量下。

3
或只是df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
muon

我尝试了@muon规定的方法。但是,我有多个带有标头的文件(标头很常见)。我不希望它们在数据框中串联。你知道我该怎么做吗?我尝试过,df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))但是给出了一个错误“ parser_f()缺少1个必需的位置参数:'filepath_or_buffer'”
cadip92

14

编辑:我用谷歌搜索https://stackoverflow.com/a/21232849/186078。但是,最近我发现使用numpy进行任何操作,然后将其分配给数据框一次,而不是在迭代的基础上操纵数据框本身,这样更快,并且似乎也可以在此解决方案中工作。

我确实希望任何访问此页面的人都考虑采用这种方法,但又不想将这段巨大的代码作为注释并使其可读性降低。

您可以利用numpy真正加快数据帧的连接速度。

import os
import glob
import pandas as pd
import numpy as np

path = "my_dir_full_path"
allFiles = glob.glob(os.path.join(path,"*.csv"))


np_array_list = []
for file_ in allFiles:
    df = pd.read_csv(file_,index_col=None, header=0)
    np_array_list.append(df.as_matrix())

comb_np_array = np.vstack(np_array_list)
big_frame = pd.DataFrame(comb_np_array)

big_frame.columns = ["col1","col2"....]

时间统计:

total files :192
avg lines per file :8492
--approach 1 without numpy -- 8.248656988143921 seconds ---
total records old :1630571
--approach 2 with numpy -- 2.289292573928833 seconds ---

有什么数字可以支持“提速”?具体来说,它比stackoverflow.com/questions/20906474/…快吗?
ivan_pozdeev '16

我没有看到OP要求加快他的级联速度的方法,这看起来像是对已有的已接受答案的重做。
pydsigner

2
如果数据具有混合的列类型,那将不起作用。
Pimin Konstantin Kefaloukos

1
@SKG完美..这是我唯一的工作解决方案。2秒内完成500个文件400k行。感谢您发布。
FrankC

11

如果要递归搜索Python 3.5或更高版本),则可以执行以下操作:

from glob import iglob
import pandas as pd

path = r'C:\user\your\path\**\*.csv'

all_rec = iglob(path, recursive=True)     
dataframes = (pd.read_csv(f) for f in all_rec)
big_dataframe = pd.concat(dataframes, ignore_index=True)

请注意,最后三行可以用一行表示:

df = pd.concat((pd.read_csv(f) for f in iglob(path, recursive=True)), ignore_index=True)

您可以在** 此处找到文档。另外,我用iglob代替glob,因为它返回一个迭代器而不是列表。



编辑:多平台递归函数:

您可以将以上内容包装到一个多平台功能(Linux,Windows,Mac)中,因此可以执行以下操作:

df = read_df_rec('C:\user\your\path', *.csv)

这是函数:

from glob import iglob
from os.path import join
import pandas as pd

def read_df_rec(path, fn_regex=r'*.csv'):
    return pd.concat((pd.read_csv(f) for f in iglob(
        join(path, '**', fn_regex), recursive=True)), ignore_index=True)

11

方便快捷

导入两个或多个csv而不需要列出名称。

import glob

df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))

8

一个衬里使用map,但是如果您要指定其他参数,则可以执行以下操作:

import pandas as pd
import glob
import functools

df = pd.concat(map(functools.partial(pd.read_csv, sep='|', compression=None), 
                    glob.glob("data/*.csv")))

注意:map本身不允许您提供其他参数。


4

如果压缩了多个csv文件,则可以使用zipfile读取全部内容并进行如下连接:

import zipfile
import numpy as np
import pandas as pd

ziptrain = zipfile.ZipFile('yourpath/yourfile.zip')

train=[]

for f in range(0,len(ziptrain.namelist())):
    if (f == 0):
        train = pd.read_csv(ziptrain.open(ziptrain.namelist()[f]))
    else:
        my_df = pd.read_csv(ziptrain.open(ziptrain.namelist()[f]))
        train = (pd.DataFrame(np.concatenate((train,my_df),axis=0), 
                          columns=list(my_df.columns.values)))

4

另一个具有列表理解功能的内联函数,它允许将参数与read_csv一起使用。

df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])

3

基于@Sid的正确答案。

串联之前,您可以将csv文件加载到中间字典中,该字典可以根据文件名(格式为dict_of_df['filename.csv'])访问每个数据集。例如,当列名未对齐时,此类词典可帮助您识别异构数据格式的问题。

导入模块并找到文件路径:

import os
import glob
import pandas
from collections import OrderedDict
path =r'C:\DRO\DCL_rawdata_files'
filenames = glob.glob(path + "/*.csv")

注意:OrderedDict不是必需的,但是它将保留文件顺序,这可能对分析有用。

将csv文件加载到字典中。然后连接:

dict_of_df = OrderedDict((f, pandas.read_csv(f)) for f in filenames)
pandas.concat(dict_of_df, sort=True)

键是文件名f,值是csv文件的数据帧内容。除了f用作字典键之外,还可以使用os.path.basename(f)或其他os.path方法将字典中键的大小减小到仅相关的较小部分。


3

使用pathlib库的替代方法(通常首选而不是os.path)。

此方法避免了pandas concat()/的迭代使用apped()

从pandas文档中:
值得注意的是,concat()(因此,append())会完整复制数据,并且不断重用此函数可能会对性能产生重大影响。如果需要对多个数据集使用该操作,请使用列表推导。

import pandas as pd
from pathlib import Path

dir = Path("../relevant_directory")

df = (pd.read_csv(f) for f in dir.glob("*.csv"))
df = pd.concat(df)

-2

这是在Google云端硬盘上使用Colab的方式

import pandas as pd
import glob

path = r'/content/drive/My Drive/data/actual/comments_only' # use your path
all_files = glob.glob(path + "/*.csv")

li = []

for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0)
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True,sort=True)
frame.to_csv('/content/drive/onefile.csv')

-3
import pandas as pd
import glob

path = r'C:\DRO\DCL_rawdata_files' # use your path
file_path_list = glob.glob(path + "/*.csv")

file_iter = iter(file_path_list)

list_df_csv = []
list_df_csv.append(pd.read_csv(next(file_iter)))

for file in file_iter:
    lsit_df_csv.append(pd.read_csv(file, header=0))
df = pd.concat(lsit_df_csv, ignore_index=True)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.