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

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

ただいまの
回答率

91.27%

  • Python

    4181questions

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

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

解決済

回答 5

投稿

  • 評価
  • クリップ 3
  • VIEW 160

Ruthird

score 6

前提・実現したいこと

以前こちらで大量にある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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

checkベストアンサー

+4

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

import glob

def is_img_file(file):
    if file.endswith('.png'): return True
    if file.endswith('.jpg'): return True
    return False

img_files = [file for file in glob.iglob('*.*') if is_img_file(file)]

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


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

import glob

search_words = input('input words: ').split()
search_mode = input('search mode(OR/AND): ')

def make_is_target(words):
    def is_target_or(filename):
        return any(word in filename for word in words)

    def is_target_and(filename):
        return all(word in filename for word in words)

    return is_target_or, is_target_and


if search_mode == 'OR':
    is_target = make_is_target(search_words)[0]
elif search_mode == 'AND':
    is_target = make_is_target(search_words)[1]
else:
    is_target = lambda _: False

target_files = [file for file in glob.iglob('*.*') if is_target(file)]
print(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/15 01:20

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

    キャンセル

  • 2017/12/15 01:30

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

    キャンセル

  • 2017/12/15 23:25

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

    キャンセル

+3

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

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

files = glob.glob('*.*')

search_words = input('input words: ').split()
search_mode = input('search mode(OR/AND default:OR): ') or 'OR'

def make_is_target(words, selection_rule=None):
    if selection_rule is None:
        print('WARNING: UNKNOWN search mode')
        return lambda _: False
    assert(selection_rule in [any, all])
    def is_target(filename):
        return selection_rule(word in filename for word in words)
    return is_target

if search_mode.upper() == 'AND':
    is_target = make_is_target(search_words, selection_rule=all)
elif search_mode.upper() != 'OR':
    is_target = make_is_target(search_words)
else:
    is_target = make_is_target(search_words, selection_rule=any)

target_files = [file for file in files if is_target(file)]
print(target_files)

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

import subprocess
run = subprocess.getoutput

search_words = input('input words: ').split()
path = '.'

target_files = run('ls {0} | grep -E "{1}"'.format(path, '|'.join(search_words))).split() # OR

print(target_files)

target_files = run('ls {0} | {1}'.format(path, ' | '.join(map('grep {0}'.format, search_words)))).split() # AND

print(target_files)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/15 23:27

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

    キャンセル

+1

from functools import partial
from glob import iglob
from operator import contains


def iglob_ex(pathname, *keywords, op=any, **kwargs):
    for f in iglob(pathname, **kwargs):
        if op(map(partial(contains, f), keywords)):
            yield f


print('# any -> OR')
for f in iglob_ex('*.py', 'wa', 'r', op=any):
    print(f)


print('# all -> AND')
for f in iglob_ex('*.py', 'x', 'te', op=all):
    print(f)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/16 00:49 編集

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

    キャンセル

+1

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

pathname = input('pathname: ')            # * など
recursive = input('recursive:') == 'True' # True (or False)
function = input('function: ')            # path.find('subdir1') >= 0 and ( basename.endswith('.txt') or basename.endswith('.jpg')) など

is_target = eval('lambda path,basename:' + function)

import glob
import os
target_files = []
for path in glob.iglob(pathname,recursive=recursive):
    if is_target(path, os.path.basename(path)):
        target_files.append(path)

print(target_files)

実行例

>python hoge.py
pathname: ./dir/**
recursive:True
function: True
['./dir\\', './dir\\subdir1', './dir\\subdir1\\test.dat', './dir\\subdir1\\test.jpg', './dir\\subdir1\\test.txt', './dir\\subdir2', './dir\\subdir2\\test.dat', './dir\\subdir2\\tes
t.jpg', './dir\\subdir2\\test.txt']

>python hoge.py
pathname: ./dir/**
recursive:True
function: path.find('subdir1') >= 0 and ( basename.endswith('.txt') or basename.endswith('.jpg'))
['./dir\\subdir1\\test.jpg', './dir\\subdir1\\test.txt']

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/16 00:29

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

    キャンセル

+1

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

import glob
import re

def inc_all(reglist, filelist):
  rr = "^"
  for i in reglist:
    rr = rr + "(?=.*" + i + ")"
  r = re.compile(rr)
  return([i for i in filter(r.search, filelist)])

def inc_one(reglist, filelist):
  rr = "(" + '|'.join(reglist) + ")"
  r = re.compile(rr)
  return([i for i in filter(r.search, filelist)])

files = glob.glob("*.*")

print(files)
print("#1", inc_all(["pl","test"], files)) 
print("#2", inc_one(["pl","test"], files))
print("#3", inc_all(["(pl|sed|txt)", "test"], files))
print("#4", inc_all(["(pl|sed|txt)", "(test|usb)", "~"], files))
  • #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/16 00:40

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

    キャンセル

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

ただいまの回答率

91.27%

関連した質問

同じタグがついた質問を見る

  • Python

    4181questions

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

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