仕事でツールの作成時苦戦してしまったので備忘録程度にメモっておきます。
経緯
データの解析の為に、フォルダに入ってるCSVを全て読み込んでその内の数列取得し、結合して2次元配列を作ったのち、リストの中のリストにある最終列が重複しているリストを除去し、CSVで書き出すツールを作りたかった。
やりたいこと
例:CSVを読み込んでこんな感じの2次元配列を作成する
[["A", "B", "C", "D", "1"], ["D", "F", "G", "A", "2"], ["O", "P", "Q", "C", "1"], ["L", "M", "T", "H", "2"], ["A", "B", "Z", "D", "3"]]
1列 | 2列 | 3列 | 4列 | 5列 |
---|---|---|---|---|
A | B | C | D | 1 |
D | F | G | A | 2 |
O | P | Q | C | 1 |
L | M | T | H | 2 |
A | B | Z | D | 3 |
この5列目の重複しているリストを以下のように除去する(1つ残ってれば順番が変わっても良い) |
[["A", "B", "C", "D", "1"], ["D", "F", "G", "A", "2"], ["A", "B", "Z", "D", "3"]]
1列 | 2列 | 3列 | 4列 | 5列 |
---|---|---|---|---|
A | B | C | D | 1 |
D | F | G | A | 2 |
A | B | Z | D | 3 |
つまり、単純にlist(set(lst))
が使えない・・・。
ということで最初は以下のような感じのコードを書いてみた。
import csv
from glob import glob
col5 = []
def main():
path = "/hogeDir"
pathList = glob(f"{path}/*.csv")
lst = []
for pl in pathList:
lst += inputCsv(pl)
print(deduplicationList(lst))
def inputCsv(path: str) -> list:
lst = []
lst2 = []
with open(path) as f:
reader = csv.reader(f)
next(reader)
for line in reader:
lst += line
lst2 += [line[4]]
col5 += line[4]
return joinList(lst, lst2)
def joinList(lst1: list, lst2: list) -> list:
tmpList = []
for i, l in zip(lst1, lst2):
tmpList = [i + l]
return tmpList
def deduplicationList(lst: list) -> list:
tmplist = lst
for i, v in enumerate(tmplist):
if v == col5[i]:
lst.pop(i)
col5.pop(i)
return lst
if __name__ == "__main__":
main()
一旦CSV全てを取得し連結した後、main()
のlst
とcol5
をぶつけ、重複をpopしようとしたが、
なんだかインデックスエラーやらそもそもlst
とcol5
の長さが一致しない。
同じCSVを読み込んで一緒にpop()してるはずなのになんだかうまくいかない・・・。
pandasを使って重複リストを除去
バグが直らず、時間がかかってしまった為、もう諦めて違う方法を考えようとWebを模索し、以下の記事を見つけた。
csv のデータが重複してるよ〜 -> それ、pandas で除けまっせ
なるほど、pandasを使えば除去できるのか!
でも2次元配列はどうなんだ…?
さらに模索した。
pandas.DataFrame, Seriesの重複した行を抽出・削除
listをpandasで読み込んでduplicated
すれば重複の一覧が取得できるのか、というか最初の記事に書いてある通りdrop_duplicates()
で消せるっぽい。
さっそく書いてみた。
import pandas as pd
def main():
path = "/hogeDir"
pathList = glob(f"{path}/*.csv")
pdlist = pd.DataFrame([], columns=["1列", "2列", "3列", "4列", "5列"])
for pl in pathList:
pd.concat(pdlist, pd.DataFrame(inputCsv(pl), columns=["1列", "2列", "3列", "4列", "5列"]))
pdlist = pdlist.drop_duplicates(subset="5列", keep="first")
pdlist.to_csv(f"{path}/output.csv", index=False)
めちゃくちゃ簡単だった…しかも約2000行読み込んでるはずなのに早い!
おとなしくpandasを使えばよかった。
まとめ
結果的にやりたいことが1行でできてしまった。しかも早い。
pandas様々
参考サイト
csv のデータが重複してるよ〜 -> それ、pandas で除けまっせ
pandas.DataFrame, Seriesの重複した行を抽出・削除
【python】辞書で同じキーに複数の値を登録する
【追記】Dictionaryを使って重複しないリストの作成
辞書型には1つのキーには1つの値しか登録されず、上書きしていきます。
つまり、CSVを読み込む時に重複したくない値をKeyとして登録していけば、重複しないリストが作成できます。
from collections import defaultdict
def main():
path = "/hogeDir"
dic = defaultdict(list)
pathList = glob(f"{path}/*.csv")
for pl in pathList:
dic.update(inputCsv(pl))
print(dic.values())
def inputCsv(path: str):
inputDic = defaultdict(list)
with open(path) as f:
reader = csv.reader(f)
next(reader)
for line in reader:
inputDic[line[4]] = line
return inputDic
今回の場合、トータル2000行以上のCSVを読み込んでいたので、その場合pandasを使った方が早いですが、
そんなに多くない場合はこの方法を使ってもいいかもしれません。