繁体   English   中英

将多个CSV文件导入pandas并拼接成一个DataFrame

[英]Import multiple CSV files into pandas and concatenate into one DataFrame

我想从一个目录中读取几个 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循环中需要一些帮助?

请参阅pandas:IO 工具了解所有可用的.read_方法。

如果您在所有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)

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
import glob
import os
import pandas as pd   
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))

这里几乎所有的答案要么过于复杂(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 个东西:

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

简单快捷

导入两个或多个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 的一个子集。 如果所有数据都适合内存,您可以调用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)

您可以在此处找到**的文档。 另外,我使用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)

灵感来自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)

笔记:

  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,当人们可能想要手动创建帧丢弃数与一分钟(或任何其他持续时间)箱的直方图并希望基于第一个时间戳进行计算时,这很有帮助,例如begin_timestamp = df['timestamp'][0]

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

如果压缩多个 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)

    

另一个带有列表理解的在线工具,它允许使用 read_csv 的参数。

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

使用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)

基于@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 pandas as pd
import os

big_frame = pd.DataFrame()

for file in os.listdir():
    if file.endswith('.csv'):
        df = pd.read_csv(file)
        big_frame = big_frame.append(df, ignore_index=True)
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 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 s10.9 spandas 在这里更快,因为它不需要压缩列来写回)。

提供的所有答案都非常好,但我发现了一种更简单的方法。

import pandas as pd
import os

files = [file for file in os.listdir('./Your_Folder')]  # Here is where all the files are located.

all_csv_files = pd.DataFrame()

for file in files:
    df = pd.read_csv("./Your_Folder/"+file)
    all_csv_files = pd.concat([all_csv_files, df])
    all_csv_files.to_csv("All_CSV_Files_Concat.csv", index=False)
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')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM