![](/img/trans.png)
[英]Combine columns from several CSV files into a single file and making multiple CSV file with for loop
[英]Combine columns from several CSV files into a single file
我有一堆CSV文件(下面的例子中只有兩個)。 每個CSV文件有6列。 我想進入每個CSV文件,復制前兩列並將它們作為新列添加到現有CSV文件中。
到目前為止,我有:
import csv
f = open('combined.csv')
data = [item for item in csv.reader(f)]
f.close()
for x in range(1,3): #example has 2 csv files, this will be automated
n=0
while n<2:
f=open(str(x)+".csv")
new_column=[item[n] for item in csv.reader(f)]
f.close()
#print d
new_data = []
for i, item in enumerate(data):
try:
item.append(new_column[i])
print i
except IndexError, e:
item.append("")
new_data.append(item)
f = open('combined.csv', 'w')
csv.writer(f).writerows(new_data)
f.close()
n=n+1
這是有效的,它不漂亮,但它的工作原理。 但是,我有三個小煩惱:
我打開每個CSV文件兩次(每列一次),這很不優雅
當我打印combined.csv
文件時,它會在每行后打印一個空行?
我必須提供一個combined.csv
文件,其中至少包含與我可能擁有的最大文件一樣多的行。 由於我真的不知道這個數字是什么,這有點糟透了
一如既往,非常感謝任何幫助!
根據要求:1.csv看起來像(模擬數據)
1,a
2,b
3,c
4,d
2.csv看起來像
5,e
6,f
7,g
8,h
9,i
combined.csv文件看起來應該是這樣的
1,a,5,e
2,b,6,f
3,c,7,g
4,d,8,h
,,9,i
import csv
import itertools as IT
filenames = ['1.csv', '2.csv']
handles = [open(filename, 'rb') for filename in filenames]
readers = [csv.reader(f, delimiter=',') for f in handles]
with open('combined.csv', 'wb') as h:
writer = csv.writer(h, delimiter=',', lineterminator='\n', )
for rows in IT.izip_longest(*readers, fillvalue=['']*2):
combined_row = []
for row in rows:
row = row[:2] # select the columns you want
if len(row) == 2:
combined_row.extend(row)
else:
combined.extend(['']*2)
writer.writerow(combined_row)
for f in handles:
f.close()
for rows in IT.izip_longest(*readers, fillvalue=['']*2):
可以用一個例子來理解:
In [1]: import itertools as IT
In [2]: readers = [(1,2,3), ('a','b','c','d'), (10,20,30,40)]
In [3]: list(IT.izip_longest(readers[0], readers[1], readers[2]))
Out[3]: [(1, 'a', 10), (2, 'b', 20), (3, 'c', 30), (None, 'd', 40)]
正如您所看到的, IT.izip_longest的行為與zip
非常相似,只是在消耗最長的可迭代之前它不會停止。 默認情況下,它使用None
填充缺少的項目。
如果readers
中有超過3個項目,會發生什么? 我們想寫
list(IT.izip_longest(readers[0], readers[1], readers[2], ...))
但這很費力,如果我們事先不知道len(readers)
,我們甚至不能用明確的東西替換省略號( ...
)。
Python有一個解決方案: star(aka參數解包)語法 :
In [4]: list(IT.izip_longest(*readers))
Out[4]: [(1, 'a', 10), (2, 'b', 20), (3, 'c', 30), (None, 'd', 40)]
注意結果Out[4]
與結果Out[3]
。
*readers
告訴Python解壓縮readers
的項目並將它們作為單獨的參數發送給IT.izip_longest
。 這就是Python允許我們向函數發送任意數量的參數的方法。
現在,似乎幾乎必須有人為Python中的任何數據處理問題提供基於熊貓的解決方案。 所以這是我的:
import pandas as pd
to_merge = ['{}.csv'.format(i) for i in range(4)]
dfs = []
for filename in to_merge:
# read the csv, making sure the first two columns are str
df = pd.read_csv(filename, header=None, converters={0: str, 1: str})
# throw away all but the first two columns
df = df.ix[:,:1]
# change the column names so they won't collide during concatenation
df.columns = [filename + str(cname) for cname in df.columns]
dfs.append(df)
# concatenate them horizontally
merged = pd.concat(dfs,axis=1)
# write it out
merged.to_csv("merged.csv", header=None, index=None)
哪個用於文件
~/coding/pand/merge$ cat 0.csv
0,a,6,5,3,7
~/coding/pand/merge$ cat 1.csv
1,b,7,6,7,0
2,c,0,1,8,7
3,d,6,8,4,5
4,e,8,4,2,4
~/coding/pand/merge$ cat 2.csv
5,f,6,2,9,1
6,g,0,3,2,7
7,h,6,5,1,9
~/coding/pand/merge$ cat 3.csv
8,i,9,1,7,1
9,j,0,9,3,9
給
In [21]: !cat merged.csv
0,a,1,b,5,f,8,i
,,2,c,6,g,9,j
,,3,d,7,h,,
,,4,e,,,,
In [22]: pd.read_csv("merged.csv", header=None)
Out[22]:
0 1 2 3 4 5 6 7
0 0 a 1 b 5 f 8 i
1 NaN NaN 2 c 6 g 9 j
2 NaN NaN 3 d 7 h NaN NaN
3 NaN NaN 4 e NaN NaN NaN NaN
我認為這是正確的對齊方式。
這是我為解決您的問題而編寫的程序。 它創建了一個類,用於保存有關要讀取的每個CSV文件的信息,包括您希望從中獲取哪些列。 然后只有一個要讀取的CSV文件列表,並從每個文件中讀取一行。
因為你說它需要保持返回行直到讀取所有輸入文件,它才會返回到達結尾的輸入文件的虛擬值。 它會一直讀取行,直到完成所有輸入文件。
此外,該程序只需要在內存中一次保持一行。 因此它甚至可以處理大型CSV文件而無需太多內存。
最初我丟失數據的虛擬值為-1。 現在我看到你添加了一個例子,你只想要沒有價值。 當沒有數據時,我已經將程序從使用-1更改為使用空字符串。
其中一個設計目標是使其可擴展。 現在你需要前兩列,但如果你以后需要其中一個文件中的第0列,第3列和第7列呢? 因此每個文件都有一個列表,其中包含要采用的列。
我實際上並沒有編寫代碼來將輸出文件重命名為原始文件名,但這很容易添加。
理想情況下,這整個事情將被包裝到一個類中,您可以在其中迭代一個類實例並使用所有輸入文件中的列返回一行。 我沒有花費額外的時間來做這件事,但如果你長期使用它,你可能想要這樣做。 此外,我從不打擾關閉任何輸入文件,因為我認為程序將在我們編寫輸出文件后結束,然后一切都會關閉; 但理想情況下,我們應該在使用它們后關閉所有文件!
import csv
fname_in = "combined.csv"
fname_out = "combined.tmp"
lst_other_fnames = [str(x) + ".csv" for x in range(1, 3)]
no_data = ''
def _no_data_list(columns):
return [no_data for _ in columns]
class DataCsvFile(object):
def __init__(self, fname, columns=None):
self.fname = fname
self.f = open(fname)
self.reader = csv.reader(self.f)
self.columns = columns
self.done = False
def next_columns(self):
if self.done:
return _no_data_list(self.columns)
try:
item = next(self.reader)
except StopIteration:
self.done = True
return _no_data_list(self.columns)
return [item[i] for i in self.columns]
# want all columns from original file
data_csv_files = [DataCsvFile(fname_in, range(5))]
# build list of filenames and columns: want first two columns from each
data_csv_files.extend(DataCsvFile(fname, range(2)) for fname in lst_other_fnames)
with open(fname_out, "w") as out_f:
writer = csv.writer(out_f)
while True:
values = []
for df in data_csv_files:
columns = df.next_columns()
values.extend(columns)
if not all(df.done for df in data_csv_files):
writer.writerow(values)
else:
break
這是一個例子(為了簡單起見,我使用字符串io而不是文件,但這不是必需的):
a = u"""
1,a
2,b
3,c
4,d
"""
b = u"""
5,e
6,f
7,g
8,h
9,i
"""
c = u"""
11,x
12,y
13,z
"""
import io, csv, itertools
data = []
expand = lambda it, size: it + [[''] * len(it[0])] * size
for f in [a, b, c]:
with io.StringIO(f.strip()) as fp:
d = list(csv.reader(fp))
t = len(d) - len(data)
data = d if not data else [
x + y for x, y in itertools.izip_longest(
expand(data, t), expand(d, -t))]
for r in data:
print ','.join(r)
# 1,a,5,e,11,x
# 2,b,6,f,12,y
# 3,c,7,g,13,z
# 4,d,8,h,,
# ,,9,i,,
使用真實文件(名為1.csv,2.csv等),主循環將如下所示:
for n in range(...):
with open(str(n) + '.csv') as fp:
d = list(csv.reader(fp))
t = len(d) - len(data)
data = d if not data else [
x + y for x, y in itertools.izip_longest(
expand(data, t), expand(d, -t))]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.