将多个CSV文件导入熊猫并连接到一个DataFrame中

我想从一个目录中读取几个CSV文件到熊猫中,并将它们连接到一个大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)

我想我在进行循环中需要一些帮助?

826432 次浏览

有关所有可用的.read_方法,请参阅熊猫:IO工具

如果所有CSV文件具有相同的列,请尝试以下代码。

我添加了header=0,以便在读取CSV文件的第一行后,可以将其分配为列名。

import pandas as pd
import glob
import os


path = r'C:\DRO\DCL_rawdata_files' # use your path
all_files = glob.glob(os.path.join(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)

或者,归因于Sid的评论。

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


df = pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)

  • 通常需要识别每个数据样本,这可以通过向数据框添加新列来完成。
  • 标准库中的pathlib将用于此示例。它将路径视为带有方法的对象,而不是要切片的字符串。

导入和设置

from pathlib import Path
import pandas as pd
import numpy as np


path = r'C:\DRO\DCL_rawdata_files'  # or unix / linux / mac path


# Get the files from the path provided in the OP
files = Path(path).glob('*.csv')  # .rglob to get subdirectories

备选案文1:

  • 使用文件名添加新列
dfs = list()
for f in files:
data = pd.read_csv(f)
# .stem is method for pathlib objects to get the filename w/o the extension
data['file'] = f.stem
dfs.append(data)


df = pd.concat(dfs, ignore_index=True)

备选案文2:

  • 使用enumerate添加具有通用名称的新列
dfs = list()
for i, f in enumerate(files):
data = pd.read_csv(f)
data['file'] = f'File {i}'
dfs.append(data)


df = pd.concat(dfs, ignore_index=True)

备选案文3:

  • 使用列表推导创建数据帧,然后使用np.repeat添加新列。
    • [f'S{i}' for i in range(len(dfs))]创建一个字符串列表来命名每个数据帧。
    • [len(df) for df in dfs]创建长度列表
  • 此选项的属性转到此绘图回答
# Read the files into dataframes
dfs = [pd.read_csv(f) for f in files]


# Combine the list of dataframes
df = pd.concat(dfs, ignore_index=True)


# Add a new column
df['Source'] = np.repeat([f'S{i}' for i in range(len(dfs))], [len(df) for df in dfs])

备选案文4:

df = pd.concat((pd.read_csv(f).assign(filename=f.stem) for f in files), ignore_index=True)

df = pd.concat((pd.read_csv(f).assign(Source=f'S{i}') for i, f in enumerate(files)), ignore_index=True)

我用谷歌搜索了一下Gaurav Singh的回答

然而,最近,我发现使用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 ---

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

如果多个CSV文件被压缩,您可以使用zipfile读取所有并连接如下:

import zipfile
import pandas as pd


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


train = []


train = [ pd.read_csv(ziptrain.open(f)) for f in ziptrain.namelist() ]


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

如果您想递归搜索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)

Dask库可以从多个文件中读取数据帧:

>>> import dask.dataframe as dd
>>> df = dd.read_csv('data*.csv')

(来源:https://examples.dask.org/dataframes/01-data-access.html#Read-CSV-files

Dask数据帧实现了Pandas数据帧API的一个子集。如果所有数据都适合内存,您可以调用df.compute()将数据帧转换为Pandas数据帧。

这里几乎所有的答案要么不必要地复杂(全局模式匹配),要么依赖于额外的第三方库。您可以使用Pandas和Python(所有版本)已经内置的所有内容在两行中完成此操作。

对于几个文件-单行

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

对于许多文件

import os


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

对于无标题

如果你有特定的东西你想改变pd.read_csv(即,没有头),你可以做一个单独的函数,并用你的map调用它:

def f(i):
return pd.read_csv(i, header=None)


df = pd.concat(map(f, filepaths))

这个设置df的熊猫系列使用了三件事:

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

使用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本身不允许您提供额外的参数。

另一个带有列表理解的单行程序,允许将参数与read_csv一起使用。

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

简单快捷

导入两个或多个CSV文件,而无需制作名称列表。

import glob
import pandas as pd


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

基于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方法将字典中键的大小减小到仅相关的较小部分。

替代使用pathlib库(通常首选于os.path)。

这种方法避免了熊猫concat()/apped()的迭代使用。

熊猫留档:
值得注意的是,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)
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)

这就是在googleDrive上使用合作的方式:

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')

你也可以这样做:

import pandas as pd
import os


new_df = pd.DataFrame()
for r, d, f in os.walk(csv_folder_path):
for file in f:
complete_file_path = csv_folder_path+file
read_file = pd.read_csv(complete_file_path)
new_df = new_df.append(read_file, ignore_index=True)




new_df.shape
import os


os.system("awk '(NR == 1) || (FNR > 1)' file*.csv > merged.csv")

其中NRFNR表示正在处理的行数。

FNR是每个文件中的当前行。

NR == 1包含第一个文件的第一行(标题),而FNR > 1跳过每个后续文件的第一行。

如果出现无名列问题,请使用此代码沿x轴合并多个CSV文件。

import glob
import os
import pandas as pd


merged_df = pd.concat([pd.read_csv(csv_file, index_col=0, header=0) for csv_file in glob.glob(
os.path.join("data/", "*.csv"))], axis=0, ignore_index=True)


merged_df.to_csv("merged.csv")

灵感来自有趣先生回答

import glob
import pandas as pd


list_of_csv_files = glob.glob(directory_path + '/*.csv')
list_of_csv_files.sort()


df = pd.concat(map(pd.read_csv, list_of_csv_files), ignore_index=True)

备注:

  1. 默认情况下,通过glob.glob生成的文件列表不排序。另一方面,在许多情况下,需要对其进行排序,例如,人们可能想要分析传感器帧丢弃v/s时间戳的数量。

  2. pd.concat命令中,如果未指定ignore_index=True,则它保留每个数据帧(即列表中的每个单独的CSV文件)的原始索引,并且主数据帧看起来像

        timestamp    id    valid_frame
    0
    1
    2
    .
    .
    .
    0
    1
    2
    .
    .
    .
    

    使用ignore_index=True,它看起来像:

        timestamp    id    valid_frame
    0
    1
    2
    .
    .
    .
    108
    109
    .
    .
    .
    

    IMO,这是有帮助的,当一个人可能想要手动创建一个直方图的帧数下降v/s一分钟(或任何其他持续时间)箱,并希望基于第一个时间戳的计算,例如。 begin_timestamp = df['timestamp'][0]

    如果没有,ignore_index=Truedf['timestamp'][0]从所有单独的数据帧生成包含第一个时间戳的序列,它不仅仅给出一个值。

考虑使用ConverTools库,它提供了大量数据处理原语并在引擎盖下生成简单的即席代码。 它不应该比熊猫/极地更快,但有时它可以。

例如,您可以将csv文件合并为一个以供进一步重用-这是代码:

import glob


from convtools import conversion as c
from convtools.contrib.tables import Table
import pandas as pd




def test_pandas():
df = pd.concat(
(
pd.read_csv(filename, index_col=None, header=0)
for filename in glob.glob("tmp/*.csv")
),
axis=0,
ignore_index=True,
)
df.to_csv("out.csv", index=False)
# took 20.9 s




def test_convtools():
table = None
for filename in glob.glob("tmp/*.csv"):
table_ = Table.from_csv(filename, header=False)
if table is None:
table = table_
else:
table = table.chain(table_)


table.into_csv("out_convtools.csv", include_header=False)
# took 15.8 s

当然,如果你只是想获得一个数据帧而不写入一个串联的文件,它将需要相应的4.63 s10.9 s熊猫在这里更快,因为它不需要压缩列来写回它)。