jak scalić 200 plików csv w Pythonie

83

Chłopaki, mam tutaj 200 oddzielnych plików csv o nazwach od SH (1) do SH (200). Chcę połączyć je w jeden plik CSV. Jak mogę to zrobić?

Gdakanie
źródło
3
W jaki sposób je scalisz? (Linie konkatenowane, ...)
tur1ng
6
Jak chcesz, aby zostały połączone? Każdy wiersz w pliku CSV to wiersz. Tak więc jedną prostą opcją jest po prostu połączenie wszystkich plików razem.
Jon-Eric
Każdy plik ma dwie kolumny. Chcę połączyć je w jeden plik z dwiema kolumnami po kolei.
Chuck,
1
@Chuck: Co powiesz na zbieranie wszystkich odpowiedzi w swoich komentarzach (na pytanie i odpowiedzi) i aktualizowanie pytania?
tumultous_rooster
2
To pytanie powinno być nazwane „Jak concat ...” zamiast „jak scalić ...”
colidyre

Odpowiedzi:

95

Jak powiedział ghostdog74, ale tym razem z nagłówkami:

fout=open("out.csv","a")
# first file:
for line in open("sh1.csv"):
    fout.write(line)
# now the rest:    
for num in range(2,201):
    f = open("sh"+str(num)+".csv")
    f.next() # skip the header
    for line in f:
         fout.write(line)
    f.close() # not really needed
fout.close()
wisty
źródło
11
możesz użyć f.__next__()zamiast tego, jeśli f.next()w python3.x.
tsveti_iko
5
Tylko uwaga: można użyć with openskładni i uniknąć ręcznego .close()wprowadzania plików.
FatihAkici
2
jaka jest różnica między f.next()i f.__next__()? kiedy używam tego pierwszego, otrzymałem'_io.TextIOWrapper' object has no attribute 'next'
Jason Goal
zanim fout.write(line)to zrobię:if line[-1] != '\n': line += '\n'
shisui
65

Dlaczego nie możesz po prostu sed 1d sh*.csv > merged.csv?

Czasami nie musisz nawet używać Pythona!

blinsay
źródło
21
W systemie Windows C: \> kopiuj * .csv scalony.csv
nalot,
6
Skopiuj informacje nagłówka z jednego pliku: sed -n 1p jakiś_plik.csv> scaled_file.csv Skopiuj wszystko oprócz ostatniej linii ze wszystkich innych plików: sed 1d * .csv >> scaled_file.csv
behas
3
@blinsay Dodaje również nagłówek w każdym pliku CSV do scalonego pliku.
Mina
5
Jak używać tego polecenia bez kopiowania informacji nagłówka dla każdego kolejnego pliku po pierwszym? Wydaje się, że wielokrotnie pojawiają się informacje z nagłówka.
Joe
2
To świetnie, jeśli nie musisz usuwać nagłówka!
Blairg23
51

Użyj zaakceptowanej odpowiedzi StackOverflow, aby utworzyć listę plików CSV, które chcesz dołączyć, a następnie uruchom ten kod:

import pandas as pd
combined_csv = pd.concat( [ pd.read_csv(f) for f in filenames ] )

A jeśli chcesz wyeksportować go do pojedynczego pliku csv, użyj tego:

combined_csv.to_csv( "combined_csv.csv", index=False )
scottlittle
źródło
@ wisty, @ Andy, przypuśćmy, że wszystkie pliki mają tytuły dla każdego wiersza - niektóre wiersze mają różne tytuły. Brak nagłówków dla dwóch kolumn w każdym pliku. Jak można scalić, tak że do każdego pliku dodawana jest tylko kolumna.
Gathide,
Gdzie plik jest eksportowany?
@ dirtysocks45, zmieniłem odpowiedź, aby była bardziej wyraźna.
scottlittle
dodaj sort: linked_csv = pd.concat ([pd.read_csv (f) for f w nazwach plików], sort = False)
sailfish009
16
fout=open("out.csv","a")
for num in range(1,201):
    for line in open("sh"+str(num)+".csv"):
         fout.write(line)    
fout.close()
ghostdog74
źródło
13

Przejdę tylko przez inny przykład kodu w koszyku

from glob import glob

with open('singleDataFile.csv', 'a') as singleFile:
    for csvFile in glob('*.csv'):
        for line in open(csvFile, 'r'):
            singleFile.write(line)
Norfeldt
źródło
2
@Andy Nie widzę różnicy między przepełnieniem stosu przypominającym mi o głosowaniu nad odpowiedzią a przypominającym ludziom, aby podzielili się uznaniem (poprzez głosowanie), jeśli uznają moją odpowiedź za przydatną. Wiem, że to nie jest Facebook i nie jestem łowcą jak łowcą ...
Norfeldt
1
Zostało to omówione wcześniej i za każdym razem uznawane było za niedopuszczalne.
Andy
10

To zależy, co masz na myśli, mówiąc „scalanie” - czy mają te same kolumny? Czy mają nagłówki? Na przykład, jeśli wszystkie mają te same kolumny i nie mają nagłówków, wystarczy prosta konkatenacja (otwórz plik docelowy do zapisu, pętla nad źródłami otwierającymi każde z nich do odczytu, użyj shutil.copyfileobj ze źródła open-for-reading do miejsce docelowe otwarte do zapisu, zamknij źródło, kontynuuj zapętlanie - użyj withinstrukcji, aby wykonać zamknięcie w swoim imieniu). Jeśli mają te same kolumny, ale także nagłówki, będziesz potrzebować znaku readlinena każdym pliku źródłowym z wyjątkiem pierwszego, po otwarciu go do odczytu przed skopiowaniem do miejsca docelowego, aby pominąć wiersz nagłówków.

Jeśli pliki CSV nie mają tych samych kolumn, musisz zdefiniować, w jakim sensie je „scalasz” (np. SQL JOIN? Lub „poziomo”, jeśli wszystkie mają tę samą liczbę wierszy? Itd.) ) - trudno nam zgadnąć, co masz na myśli w tym przypadku.

Alex Martelli
źródło
Każdy plik ma dwie kolumny z nagłówkami. Chcę połączyć je w jeden plik z dwiema kolumnami po kolei.
Chuck
4

Niewielka zmiana w powyższym kodzie, ponieważ w rzeczywistości nie działa on poprawnie.

Powinno wyglądać następująco ...

from glob import glob

with open('main.csv', 'a') as singleFile:
    for csv in glob('*.csv'):
        if csv == 'main.csv':
            pass
        else:
            for line in open(csv, 'r'):
                singleFile.write(line)
Adders
źródło
3

Jeśli scalony globplik CSV ma być używany w Pythonie, po prostu użyj, aby uzyskać listę plików do przekazania fileinput.input()za pośrednictwem filesargumentu, a następnie użyj csvmodułu, aby przeczytać wszystko za jednym razem.

Ignacio Vazquez-Abrams
źródło
3

Całkiem łatwo połączyć wszystkie pliki w katalogu i scalić je

import glob
import csv


# Open result file
with open('output.txt','wb') as fout:
    wout = csv.writer(fout,delimiter=',') 
    interesting_files = glob.glob("*.csv") 
    h = True
    for filename in interesting_files: 
        print 'Processing',filename 
        # Open and process file
        with open(filename,'rb') as fin:
            if h:
                h = False
            else:
                fin.next()#skip header
            for line in csv.reader(fin,delimiter=','):
                wout.writerow(line)
varun
źródło
3

Jeśli pracujesz na Linuksie / Macu, możesz to zrobić.

from subprocess import call
script="cat *.csv>merge.csv"
call(script,shell=True)
słoneczny
źródło
2

LUB, możesz po prostu zrobić

cat sh*.csv > merged.csv
Nanashi No Gombe
źródło
1

Możesz zaimportować csv, a następnie przejrzeć wszystkie pliki CSV i odczytać je na liście. Następnie zapisz listę z powrotem na dysk.

import csv

rows = []

for f in (file1, file2, ...):
    reader = csv.reader(open("f", "rb"))

    for row in reader:
        rows.append(row)

writer = csv.writer(open("some.csv", "wb"))
writer.writerows("\n".join(rows))

Powyższe nie jest bardzo niezawodne, ponieważ nie obsługuje błędów ani nie zamyka żadnych otwartych plików. Powinno to działać niezależnie od tego, czy poszczególne pliki mają jeden lub więcej wierszy danych CSV. Nie uruchomiłem również tego kodu, ale powinien dać ci pomysł, co robić.

cnobile
źródło
1

W przypadku rozwiązania, które sprawiło, że @Adders i później zostało ulepszone przez @varun, zaimplementowałem trochę ulepszeń, pozostawiając cały scalony plik CSV tylko z głównym nagłówkiem:

from glob import glob

filename = 'main.csv'

with open(filename, 'a') as singleFile:
    first_csv = True
    for csv in glob('*.csv'):
        if csv == filename:
            pass
        else:
            header = True
            for line in open(csv, 'r'):
                if first_csv and header:
                    singleFile.write(line)
                    first_csv = False
                    header = False
                elif header:
                    header = False
                else:
                    singleFile.write(line)
    singleFile.close()

Z poważaniem!!!

Fabián Miranda Muñoz
źródło
1

Możesz po prostu skorzystać z wbudowanej csvbiblioteki. To rozwiązanie zadziała, nawet jeśli niektóre z Twoich plików CSV mają nieco inne nazwy kolumn lub nagłówki, w przeciwieństwie do innych odpowiedzi najczęściej wybieranych.

import csv
import glob


filenames = [i for i in glob.glob("SH*.csv")]
header_keys = []
merged_rows = []

for filename in filenames:
    with open(filename) as f:
        reader = csv.DictReader(f)
        merged_rows.extend(list(reader))
        header_keys.extend([key for key in reader.fieldnames if key not in header_keys])

with open("combined.csv", "w") as f:
    w = csv.DictWriter(f, fieldnames=header_keys)
    w.writeheader()
    w.writerows(merged_rows)

Scalony plik będzie zawierał wszystkie możliwe kolumny ( header_keys), które można znaleźć w plikach. Wszelkie brakujące kolumny w pliku byłyby renderowane jako puste / puste (ale z zachowaniem reszty danych pliku).

Uwaga:

  • To nie zadziała, jeśli Twoje pliki CSV nie mają nagłówków. W takim przypadku możesz nadal korzystać z csvbiblioteki, ale zamiast używać DictReader& DictWriter, będziesz musiał pracować z podstawowym reader& writer.
  • Może to powodować problemy, gdy masz do czynienia z ogromnymi danymi, ponieważ cała zawartość jest przechowywana w pamięci ( merged_rowslista).
shad0w_wa1k3r
źródło
0

Zmodyfikowałem to, co @wisty powiedział, że działa z pythonem 3.x, dla tych z Was, którzy mają problem z kodowaniem, również używam modułu os, aby uniknąć twardego kodowania

import os 
def merge_all():
    dir = os.chdir('C:\python\data\\')
    fout = open("merged_files.csv", "ab")
    # first file:
    for line in open("file_1.csv",'rb'):
        fout.write(line)
    # now the rest:
    list = os.listdir(dir)
    number_files = len(list)
    for num in range(2, number_files):
        f = open("file_" + str(num) + ".csv", 'rb')
        f.__next__()  # skip the header
        for line in f:
            fout.write(line)
        f.close()  # not really needed
    fout.close()
Maryam Pashmi
źródło
0

Oto skrypt:

  • Łączenie plików CSV o nazwie SH1.csv doSH200.csv
  • Zachowanie nagłówków
import glob
import re

# Looking for filenames like 'SH1.csv' ... 'SH200.csv'
pattern = re.compile("^SH([1-9]|[1-9][0-9]|1[0-9][0-9]|200).csv$")
file_parts = [name for name in glob.glob('*.csv') if pattern.match(name)]

with open("file_merged.csv","wb") as file_merged:
    for (i, name) in enumerate(file_parts):
        with open(name, "rb") as file_part:
            if i != 0:
                next(file_part) # skip headers if not first file
            file_merged.write(file_part.read())
x0s
źródło
0

Aktualizuję odpowiedź Wisty dla Pythona3

fout=open("out.csv","a")
# first file:
for line in open("sh1.csv"):
    fout.write(line)
# now the rest:    
for num in range(2,201):
    f = open("sh"+str(num)+".csv")
    next(f) # skip the header
    for line in f:
         fout.write(line)
    f.close() # not really needed
fout.close()
ishandutta2007
źródło
0

Powiedzmy, że masz 2 csv takie pliki:

csv1.csv:

id,name
1,Armin
2,Sven

csv2.csv:

id,place,year
1,Reykjavik,2017
2,Amsterdam,2018
3,Berlin,2019

i chcesz, aby wynik był taki jak ten csv3.csv:

id,name,place,year
1,Armin,Reykjavik,2017
2,Sven,Amsterdam,2018
3,,Berlin,2019

W tym celu możesz użyć następującego fragmentu kodu:

import csv
import pandas as pd

# the file names
f1 = "csv1.csv"
f2 = "csv2.csv"
out_f = "csv3.csv"

# read the files
df1 = pd.read_csv(f1)
df2 = pd.read_csv(f2)

# get the keys
keys1 = list(df1)
keys2 = list(df2)

# merge both files
for idx, row in df2.iterrows():
    data = df1[df1['id'] == row['id']]

    # if row with such id does not exist, add the whole row
    if data.empty:
        next_idx = len(df1)
        for key in keys2:
            df1.at[next_idx, key] = df2.at[idx, key]

    # if row with such id exists, add only the missing keys with their values
    else:
        i = int(data.index[0])
        for key in keys2:
            if key not in keys1:
                df1.at[i, key] = df2.at[idx, key]

# save the merged files
df1.to_csv(out_f, index=False, encoding='utf-8', quotechar="", quoting=csv.QUOTE_NONE)

Za pomocą pętli możesz osiągnąć ten sam wynik dla wielu plików, jak w twoim przypadku (200 plików csv).

tsveti_iko
źródło
0

Jeśli pliki nie są ponumerowane w kolejności, zastosuj bezproblemowe podejście poniżej: Python 3.6 na komputerze z systemem Windows:

import pandas as pd
from glob import glob

interesting_files = glob("C:/temp/*.csv") # it grabs all the csv files from the directory you mention here

df_list = []
for filename in sorted(interesting_files):

df_list.append(pd.read_csv(filename))
full_df = pd.concat(df_list)

# save the final file in same/different directory:
full_df.to_csv("C:/temp/merged_pandas.csv", index=False)
Azadeh Feizpour
źródło
0

Łatwa w użyciu funkcja:

def csv_merge(destination_path, *source_paths):
'''
Merges all csv files on source_paths to destination_path.
:param destination_path: Path of a single csv file, doesn't need to exist
:param source_paths: Paths of csv files to be merged into, needs to exist
:return: None
'''
with open(destination_path,"a") as dest_file:
    with open(source_paths[0]) as src_file:
        for src_line in src_file.read():
            dest_file.write(src_line)
    source_paths.pop(0)
    for i in range(len(source_paths)):
        with open(source_paths[i]) as src_file:
            src_file.next()
            for src_line in src_file:
                 dest_file.write(src_line)
Ken Shibata
źródło
0
import pandas as pd
import os

df = pd.read_csv("e:\\data science\\kaggle assign\\monthly sales\\Pandas-Data-Science-Tasks-master\\SalesAnalysis\\Sales_Data\\Sales_April_2019.csv")
files = [file for file in  os.listdir("e:\\data science\\kaggle assign\\monthly sales\\Pandas-Data-Science-Tasks-master\\SalesAnalysis\\Sales_Data")
for file in files:
    print(file)

all_data = pd.DataFrame()
for file in files:
    df=pd.read_csv("e:\\data science\\kaggle assign\\monthly sales\\Pandas-Data-Science-Tasks-master\\SalesAnalysis\\Sales_Data\\"+file)
    all_data = pd.concat([all_data,df])
    all_data.head()
Sunit Deogam
źródło