LoginSignup
2
2

More than 5 years have passed since last update.

CSVを読み込んで重複した要素を含むlistを除去したい

Last updated at Posted at 2019-03-14

仕事でツールの作成時苦戦してしまったので備忘録程度にメモっておきます。

経緯

データの解析の為に、フォルダに入ってる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()lstcol5をぶつけ、重複をpopしようとしたが、
なんだかインデックスエラーやらそもそもlstcol5の長さが一致しない。
同じCSVを読み込んで一緒にpop()してるはずなのになんだかうまくいかない・・・。

pandasを使って重複リストを除去

バグが直らず、時間がかかってしまった為、もう諦めて違う方法を考えようとWebを模索し、以下の記事を見つけた。
csv のデータが重複してるよ〜 -> それ、pandas で除けまっせ

なるほど、pandasを使えば除去できるのか!
でも2次元配列はどうなんだ…?:thinking:
さらに模索した。
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様々:pray:

参考サイト

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を使った方が早いですが、
そんなに多くない場合はこの方法を使ってもいいかもしれません。

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2