我想将目录中的几个 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 循环中需要一些帮助???
有关所有可用的 .read_
方法,请参阅 pandas: IO tools。
如果您的所有 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] 创建一个长度列表
[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:
一个使用 .assign 创建新列的衬里,归因于来自 C8H10N4O2 的评论
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)
darindaCoder's answer 的替代方案:
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
pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)
。 Pandas 0.18.1 版需要内括号
import glob
import os
import pandas as pd
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))
df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
functools.partial
,以避免 lambda
这里几乎所有的答案要么过于复杂(glob 模式匹配),要么依赖于额外的 3rd 方库。您可以使用 Pandas 和 python(所有版本)已经内置的所有内容在 2 行中完成此操作。
对于一些文件 - 1 班轮
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 更改的特定内容(即没有标题),您可以创建一个单独的函数并使用您的地图调用它:
def f(i):
return pd.read_csv(i, header=None)
df = pd.concat(map(f, filepaths))
这条设置 df 的 pandas 行利用了 3 个东西:
Python 的映射(函数,可迭代)向函数(pd.read_csv())发送可迭代(我们的列表),它是文件路径中的每个 csv 元素)。 Panda 的 read_csv() 函数正常读取每个 CSV 文件。 Panda 的 concat() 将所有这些都放在一个 df 变量下。
df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv))
,但它给出了一个错误“parser_f() 缺少 1 个必需的位置参数:'filepath_or_buffer'”
简单快捷
导入两个或多个 csv
,而无需制作名称列表。
import glob
import pandas as pd
df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))
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 的一个子集。如果所有数据都适合内存,您可以 call df.compute()
将数据帧转换为 Pandas 数据帧。
编辑:我用谷歌搜索进入 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 ---
一个使用 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
本身不允许您提供额外的参数。
如果要递归搜索(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)
您可以找到 **
here 的文档。另外,我使用了 iglob
而不是 glob
,因为它返回的是 iterator 而不是列表。
编辑:多平台递归函数:
您可以将上述内容包装成一个多平台功能(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)
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)
笔记:
默认情况下,通过 glob.glob 生成的文件列表是不排序的。另一方面,在许多情况下,需要对其进行排序,例如,可能需要分析传感器帧丢弃数v/s 时间戳。在 pd.concat 命令中,如果未指定 ignore_index=True ,则它保留每个数据帧(即列表中的每个单独的 CSV 文件)的原始索引,并且主数据帧看起来像时间戳 id valid_frame 0 1 2 。 . . 0 1 2 。 . .使用ignore_index=True,它看起来像:timestamp id valid_frame 0 1 2。 . . 108 109 。 . . IMO,当人们可能想要手动创建帧丢弃数与一分钟(或任何其他持续时间)箱的直方图并希望基于第一个时间戳进行计算时,这很有帮助,例如 begin_timestamp = df['timestamp'] [0] 没有,ignore_index=True, df['timestamp'][0] 生成包含所有单个数据帧的第一个时间戳的系列,它不只给出一个值。
另一个带有列表理解的在线工具,它允许使用 read_csv 的参数。
df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])
如果压缩多个 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)
使用 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)
基于@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 方法将字典中的键的大小减少到仅相关的较小部分。
import os
os.system("awk '(NR == 1) || (FNR > 1)' file*.csv > merged.csv")
其中 NR
和 FNR
表示正在处理的行号。
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 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
考虑使用 convtools 库,它提供了大量数据处理原语并在后台生成简单的临时代码。它不应该比 pandas/polars 快,但有时可以。
例如,您可以将 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 s
和 10.9 s
(pandas 在这里更快,因为它不需要压缩列来写回 em>)。
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)
这是在 Google Drive 上使用 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')
df = pd.concat((pd.read_csv(f) for f in all_files))
此外,也许应该使用os.path.join(path, "*.csv")
而不是path + "/*.csv"
,这使得它独立于操作系统。