14

たくさんのCSVファイルがあります(以下の例では2つだけです)。各CSVファイルには6つの列があります。各CSVファイルに移動し、最初の2つの列をコピーして、既存の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

これは機能します、きれいではありませんが、機能します。ただし、私には3つの小さな問題があります。

  1. 各CSVファイルを2回(各列に1回)開きますが、これはほとんどエレガントではありません。

  2. ファイルを印刷すると、combined.csv各行の後に空の行が印刷されますか?

  3. 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
4

4 に答える 4

7

最近では、誰かがPython でのデータ処理の問題に対してpandasベースのソリューションを提供することがほぼ義務付けられているようです。だからここに私のものがあります:

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

これは正しい配置だと思います。

于 2013-01-26T00:00:45.183 に答える
7
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_row.extend(['']*2)#This extends two empty columns
        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、最長の iterable が消費されるまで停止しない点が異なります。不足している項目はデフォルトで で埋めNoneられます。

に 3 つ以上のアイテムがあった場合はどうなるreadersでしょうか。書きたいと思います

list(IT.izip_longest(readers[0], readers[1], readers[2], ...))

しかし、それは骨の折れる作業であり、事前に知らなければ、省略記号 ( ) を明示的なものlen(readers)に置き換えることさえできません。...

Python にはこれに対する解決策があります:スター (別名引数のアンパック) 構文:

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]

*readersPython にアイテムをアンパックし、readers個々の引数として に送信するように指示しIT.izip_longestます。これは、Python が任意の数の引数を関数に送信できるようにする方法です。

于 2013-01-25T23:25:44.170 に答える
3

これがあなたの問題を解決するために私が書いたプログラムです。必要な列を含む、読み取る各 CSV ファイルに関する情報を保持するクラスを作成します。次に、読み取る CSV ファイルのリストがあり、それぞれから 1 行が読み取られます。

すべての入力ファイルが読み込まれるまで行を返し続ける必要があると言ったので、最後に達した入力ファイルのダミー値を返します。すべての入力ファイルが完了するまで行を読み取り続けます。

また、このプログラムは一度に 1 つの行をメモリに保持するだけで済みます。そのため、多くのメモリを必要とせずに、大きな CSV ファイルでも処理できます。

もともと、欠損データのダミー値は -1 でした。今、あなたが例を追加したことがわかりましたが、値が必要ないだけです。データがない場合、プログラムを -1 の使用から空の文字列の使用に変更しました。

設計目標の 1 つは、これを拡張可能にすることでした。現時点では最初の 2 列が必要ですが、後でいずれかのファイルの列 0、3、および 7 が必要になった場合はどうでしょうか。したがって、各ファイルには、取得する列のリストがあります。

出力ファイルの名前を元のファイル名に変更するコードは実際には書きませんでしたが、簡単に追加できます。

理想的には、このすべてがクラスにラップされ、そこでクラス インスタンスを反復し、すべての入力ファイルの列を使用して 1 つの行をまとめることができます。私はそれをするのに余分な時間はかかりませんでしたが、これを長期間使用する場合は、それをしたいと思うかもしれません. また、出力ファイルを書き込んだ後にプログラムが終了し、すべてが閉じられると考えているため、入力ファイルを閉じる必要はありませんでした。しかし理想的には、使用後にすべてのファイルを閉じる必要があります。

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
于 2013-01-25T22:38:39.383 に答える
1

例を次に示します (簡単にするためにファイルの代わりに文字列 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))]
于 2013-01-25T21:55:39.060 に答える