将多个 CSV 文件导入 pandas 并连接成一个 DataFrame
- 2024-11-20 08:44:00
- admin 原创
- 9
问题描述:
我想将目录中的几个 CSV 文件读入 pandas 并将它们连接成一个大的 DataFrame。但我还没能弄清楚。以下是我目前所拥有的:
import glob
import pandas as pd
# Get data file names
path = r'C:DRODCL_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循环中的一些帮助?
解决方案 1:
请参阅pandas:IO 工具,了解所有可用.read_
方法。
如果所有 CSV 文件都有相同的列,请尝试以下代码。
我已经添加了header=0
,以便在读取 CSV 文件的第一行后,可以将其指定为列名。
import pandas as pd
import glob
import os
path = r'C:DRODCL_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:DRODCL_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:
使用一行代码
.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)
解决方案 2:
darindaCoder 答案的替代方案:
path = r'C:DRODCL_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
解决方案 3:
import glob
import os
import pandas as pd
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))
解决方案 4:
这里几乎所有的答案要么不必要地复杂(全局模式匹配),要么依赖于额外的第三方库。您可以使用 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 更改特定内容(即没有标题),您可以创建一个单独的函数并使用您的地图调用它:
def f(i):
return pd.read_csv(i, header=None)
df = pd.concat(map(f, filepaths))
设置 df 的这个 pandas 行利用了三件事:
Python 的 map(函数,可迭代)将可迭代对象(我们的列表)发送给函数(
pd.read_csv()
文件路径中的每个 CSV 元素)。Panda 的read_csv()函数正常读取每个 CSV 文件。
Panda 的concat()将所有这些置于一个 df 变量之下。
解决方案 5:
简便快捷
导入两个或多个 CSV 文件,无需列出姓名。
import glob
import pandas as pd
df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))
解决方案 6:
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 数据框。
解决方案 7:
我通过 Google 找到了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 ---
解决方案 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
它本身不允许您提供额外的参数。
解决方案 9:
如果要递归搜索(Python 3.5 或更高版本),可以执行以下操作:
from glob import iglob
import pandas as pd
path = r'C:/useryourpath***.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:/useryourpath', *.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)
解决方案 10:
受到MrFun的回答的启发:
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
未排序。另一方面,在许多情况下,需要排序,例如,可能需要分析传感器丢帧数与时间戳。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
.
.
.
在我看来,这在人们可能想要手动创建帧丢失数量与一分钟(或任何其他持续时间)箱的直方图并希望基于第一个时间戳进行计算时很有用,例如begin_timestamp = df['timestamp'][0]
如果没有ignore_index=True
,df['timestamp'][0]
则会生成包含所有单个数据帧中第一个时间戳的系列,它不仅仅给出一个值。
解决方案 11:
另一个具有列表理解的单行代码,允许使用read_csv的参数。
df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])
解决方案 12:
替代使用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)
解决方案 13:
如果压缩了多个 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)
解决方案 14:
根据Sid的良好回答。
识别缺失或未对齐列的问题
在连接之前,您可以将 CSV 文件加载到中间字典中,该字典可以根据文件名(格式)访问每个数据集dict_of_df['filename.csv']
。例如,当列名不一致时,这样的字典可以帮助您识别异构数据格式的问题。
导入模块并定位文件路径:
import os
import glob
import pandas
from collections import OrderedDict
path =r'C:DRODCL_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方法将字典中键的大小减少到仅相关的较小部分。
解决方案 15:
import os
os.system("awk '(NR == 1) || (FNR > 1)' file*.csv > merged.csv")
其中NR
和FNR
代表正在处理的行号。
FNR
是每个文件中的当前行。
NR == 1
包括第一个文件的第一行(标题),同时FNR > 1
跳过每个后续文件的第一行。
解决方案 16:
如果出现未命名列问题,请使用此代码沿 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")
解决方案 17:
您也可以按照下面的方法进行:
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
解决方案 18:
考虑使用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在这里更快,因为它不需要 zip 列来将其写回)。
解决方案 19:
import pandas as pd
import glob
path = r'C:DRODCL_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)
解决方案 20:
你可以使用Google Drive上的Colaboratory进行以下操作:
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')
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件