質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

5回答

837閲覧

大量のデータファイルを読み込む際に ファイル名入力することで 絞り込みする機能(and/or) の構築は可能でしょうか?

Ruthird

総合スコア15

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

3クリップ

投稿2017/12/14 15:47

###前提・実現したいこと

以前こちらで大量にあるCSVファイルの中から任意のデータを抽出する方法を質問しました。
その際は glob を使って ディレクトリ内のデータを一括で取得する方法を教わりました。

その延長で扱うデータファイルの数自体が更に増えてしまい、
すべてのファイルを一気に取得すること自体が効率的でなくなってしまいました。

前提として、ファイル名が含んでいる任意の単語を入力して
必要なファイルの候補を絞り込むフィルタ機能を作成したいが、
今後さらに増えることも予測して 汎用性の高い データファイルの 選択機能を用意したい、

と考えています。

###発生している問題・エラーメッセージ

現状は 1単語だけの候補絞り込みができていますが、
複数の単語でのand検索、
または複数単語で1つでも該当するものを見つけるor検索
的なものへの発展ができないか検討しています。

###該当のソースコード・試したこと

具体的に、指定ディレクトリ内のCSVファイル を すべて取り込もうとした際、

import glob files = glob.glob('dir/*.csv')

としていましたが、

import glob word = input('ファイル名 の 検索ワードを入力 (入力ない場合は全読込み) : ') files = glob.glob('dir/*'+ word +'*.csv')

と変えることで、簡単ですが1回だけ任意の単語を入力できるようにするだけで
大分候補を絞れて使いやすくはなりました。

ですが、これだと
・2つ以上の単語を入力してすべてに該当するファイルの絞り込み (and)
・2つ以上の単語を入力してどれか1つでも単語に該当するファイルの検索 (or)
が実現できないため、
もっと良い方法はないものか考えています。

###補足情報(言語/FW/ツール等のバージョンなど)
Python 3.6.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

glob.iglobを用いるとメモリを食いませんし、融通が効くかもしれません。
以下、即興で書いた使用例です。

Python

1import glob 2 3def is_img_file(file): 4 if file.endswith('.png'): return True 5 if file.endswith('.jpg'): return True 6 return False 7 8img_files = [file for file in glob.iglob('*.*') if is_img_file(file)]

かなり泥臭く、もっとすっきり書く方法もあるような気もしますが、小回りは利くかと思います。


例をちょっと改良してみました。

Python

1import glob 2 3search_words = input('input words: ').split() 4search_mode = input('search mode(OR/AND): ') 5 6def make_is_target(words): 7 def is_target_or(filename): 8 return any(word in filename for word in words) 9 10 def is_target_and(filename): 11 return all(word in filename for word in words) 12 13 return is_target_or, is_target_and 14 15 16if search_mode == 'OR': 17 is_target = make_is_target(search_words)[0] 18elif search_mode == 'AND': 19 is_target = make_is_target(search_words)[1] 20else: 21 is_target = lambda _: False 22 23target_files = [file for file in glob.iglob('*.*') if is_target(file)] 24print(target_files)

実行例

>python hoge.py input words: java py search mode(OR/AND): OR ['Bikkuri.java', 'classtree.py', 'hoge.py', 'Main.java'] >python hoge.py input words: java py search mode(OR/AND): AND [] >python hoge.py input words: hoge py search mode(OR/AND): AND ['hoge.py']

投稿2017/12/14 15:59

編集2017/12/14 17:08
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruthird

2017/12/14 16:20

or機能は 意図どおりの動きです。 内容の租借に時間がかかりそうですが、ひとまずありがとうございました。助かります。 and機能 も構築可能でしたら サポートをお願いいたします。
LouiS0616

2017/12/14 16:30

いろいろ粗があるので、適宜修正して使ってみてください。
Ruthird

2017/12/15 14:25

And機能についてもすぐにご回答いただきありがとうございました。 早速業務でも取り入れてうまく機能しましたので、ベストアンサーにさせていただきました。 まだまだ初心者のため、内容の理解は今後継続してやってみようと思います。
guest

0

LouiS0616さんの改良コードに、無駄な機能を追加してみました。

  1. サーチモードのデフォルト(OR)を追加しました
  2. サーチモード選択をcase-insensitiveにしました
  3. make_is_targetの移植性を高めました

python

1import glob 2 3files = glob.glob('*.*') 4 5search_words = input('input words: ').split() 6search_mode = input('search mode(OR/AND default:OR): ') or 'OR' 7 8def make_is_target(words, selection_rule=None): 9 if selection_rule is None: 10 print('WARNING: UNKNOWN search mode') 11 return lambda _: False 12 assert(selection_rule in [any, all]) 13 def is_target(filename): 14 return selection_rule(word in filename for word in words) 15 return is_target 16 17if search_mode.upper() == 'AND': 18 is_target = make_is_target(search_words, selection_rule=all) 19elif search_mode.upper() != 'OR': 20 is_target = make_is_target(search_words) 21else: 22 is_target = make_is_target(search_words, selection_rule=any) 23 24target_files = [file for file in files if is_target(file)] 25print(target_files)

LinuxやMacならシェルに頑張っていただくこともできます。

python

1import subprocess 2run = subprocess.getoutput 3 4search_words = input('input words: ').split() 5path = '.' 6 7target_files = run('ls {0} | grep -E "{1}"'.format(path, '|'.join(search_words))).split() # OR 8 9print(target_files) 10 11target_files = run('ls {0} | {1}'.format(path, ' | '.join(map('grep {0}'.format, search_words)))).split() # AND 12 13print(target_files)

投稿2017/12/14 19:47

編集2017/12/15 10:21
mkgrei

総合スコア8560

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruthird

2017/12/15 14:27

実際に使ってみて デフォルトの or機能 はとても便利でした。 ご回答ありがとうございます。
guest

0

勉強中の人間としてはみなさんの回答にそれぞれうろこが落ちる思いです。
が、ここで敢えて違う方法に挑戦してみたいと思います。

python

1import glob 2import re 3 4def inc_all(reglist, filelist): 5 rr = "^" 6 for i in reglist: 7 rr = rr + "(?=.*" + i + ")" 8 r = re.compile(rr) 9 return([i for i in filter(r.search, filelist)]) 10 11def inc_one(reglist, filelist): 12 rr = "(" + '|'.join(reglist) + ")" 13 r = re.compile(rr) 14 return([i for i in filter(r.search, filelist)]) 15 16files = glob.glob("*.*") 17 18print(files) 19print("#1", inc_all(["pl","test"], files)) 20print("#2", inc_one(["pl","test"], files)) 21print("#3", inc_all(["(pl|sed|txt)", "test"], files)) 22print("#4", inc_all(["(pl|sed|txt)", "(test|usb)", "~"], files)) 23
  • #1: "pl" "test"を全て含むファイル名を列挙
  • #2: "pl" "test"のいずれかを含むファイル名を列挙
  • #3: "pl" "sed "txt" のいずれかと"test"を全て含むファイル名を列挙
  • #4: "pl" "sed "txt" のいずれかと"test" "usb"のいずれかと"~"を全て含むファイル名を列挙

実行するとこんな感じ

['test.py', 'test.pl', 'test.py~', 'test.pl~', 'out.aa', 'test.sh~', 'out.af', 'test.c~', 'test.r', 'test.csv~', 'my_r.r', 'test.sed', 'test.sh', 'out.ae', 'test.txt', 'test.c', 'out.ac', 'daytest.r', 'remount_usb.txt', 'remount_usb.sh', 'test.zip', 'test.r~', 'remount_usb.pl~', 'test.csv', 'out.ad', 'test.sed~', 'remount_usb.pl', 'out.ab'] #1 ['test.pl', 'test.pl~'] #2 ['test.py', 'test.pl', 'test.py~', 'test.pl~', 'test.sh~', 'test.c~', 'test.r', 'test.csv~', 'test.sed', 'test.sh', 'test.txt', 'test.c', 'daytest.r', 'test.zip', 'test.r~', 'remount_usb.pl~', 'test.csv', 'test.sed~', 'remount_usb.pl'] #3 ['test.pl', 'test.pl~', 'test.sed', 'test.txt', 'test.sed~'] #4 ['test.pl~', 'remount_usb.pl~', 'test.sed~']

(a|b)を許容するならinc_oneはわざわざ用意する必要ないという説もありますが…

投稿2017/12/15 08:54

KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruthird

2017/12/15 15:40

かなり多様な組み合わせの検索ができるのですね。 もっと扱うデータの量が多くなってきたら活用できそうです。 参考にさせていただきます。ありがとうございます。
guest

0

自分しか使わないならeval絞り込みコードを直指定するという荒業も。
スクリプト書き換えた方が早(略

Python

1pathname = input('pathname: ') # * など 2recursive = input('recursive:') == 'True' # True (or False) 3function = input('function: ') # path.find('subdir1') >= 0 and ( basename.endswith('.txt') or basename.endswith('.jpg')) など 4 5is_target = eval('lambda path,basename:' + function) 6 7import glob 8import os 9target_files = [] 10for path in glob.iglob(pathname,recursive=recursive): 11 if is_target(path, os.path.basename(path)): 12 target_files.append(path) 13 14print(target_files)

実行例

Plain

1>python hoge.py 2pathname: ./dir/** 3recursive:True 4function: True 5['./dir\', './dir\subdir1', './dir\subdir1\test.dat', './dir\subdir1\test.jpg', './dir\subdir1\test.txt', './dir\subdir2', './dir\subdir2\test.dat', './dir\subdir2\tes 6t.jpg', './dir\subdir2\test.txt'] 7 8>python hoge.py 9pathname: ./dir/** 10recursive:True 11function: path.find('subdir1') >= 0 and ( basename.endswith('.txt') or basename.endswith('.jpg')) 12['./dir\subdir1\test.jpg', './dir\subdir1\test.txt']

投稿2017/12/15 01:32

can110

総合スコア38256

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruthird

2017/12/15 15:29

試してみました。 確かに自分は初心者なのでディレクトリなんかは決め打ちしているから、毎回入力するメリットは・・・。。。 とはいえ、汎用性が高い、と自分では言っているので、これはこれでとても参考になります。 ありがとうございます。
guest

0

python

1from functools import partial 2from glob import iglob 3from operator import contains 4 5 6def iglob_ex(pathname, *keywords, op=any, **kwargs): 7 for f in iglob(pathname, **kwargs): 8 if op(map(partial(contains, f), keywords)): 9 yield f 10 11 12print('# any -> OR') 13for f in iglob_ex('*.py', 'wa', 'r', op=any): 14 print(f) 15 16 17print('# all -> AND') 18for f in iglob_ex('*.py', 'x', 'te', op=all): 19 print(f)

投稿2017/12/15 01:18

YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruthird

2017/12/15 15:49 編集

今自分が検証しているデータ群ではうまく結果がでませんでした。 扱い方を間違えているのかも…ゆっくり読ませていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問