9

CSV ファイルに多数の数値配列が書き込まれており、その配列のスライスのみを読み込む必要があります。np.genfromtxt()概念的には、結果の配列を呼び出して行スライスしたいのですが、

  1. ファイルが大きすぎて RAM に収まらない可能性があります
  2. 関連する行の数が少ない可能性があるため、すべての行を解析する必要はありません。

MATLAB にはtextscan()、ファイル記述子を取り、ファイルのチャンクのみを読み取ることができる関数があります。NumPyにそのようなものはありますか?

今のところ、指定された条件を満たす行のみを読み取る次の関数を定義しました。

def genfromtxt_cond(fname, cond=(lambda str: True)):
  res = []
  with open(fname) as file:
    for line in file:
      if cond(line):
        res.append([float(s) for s in line.split()])

  return np.array(res, dtype=np.float64)

このソリューションにはいくつかの問題があります。

  • 一般的ではない: float 型のみをサポートし、genfromtxt列ごとに異なる可能性がある型を検出します。また、欠損値、コンバーター、スキップなど。
  • 効率的ではない: 条件が難しい場合、すべての行が 2 回解析される可能性があり、使用されるデータ構造と読み取りバッファも最適ではない可能性があります。
  • コードを書く必要があります。

フィルタリングを実装する標準関数、または MATLAB の対応する関数はありtextscanますか?

4

3 に答える 3

16

あなたが求めている機能のいくつかを提供する2つのアプローチを考えることができます:

  1. チャンク/またはn行のストライド/などのいずれかでファイルを読み取るには:
    ageneratornumpy.genfromtxt および numpy.loadtxt渡すことができます。このようにして、2 つの関数の便利な解析機能をすべて保持しながら、メモリ効率的にテキスト ファイルから大きなデータセットを読み込むことができます。

  2. 正規表現として表現できる基準に一致する行からのみデータを読み取るには: numpy.fromregexを使用し、 a
    を使用して、入力ファイル内の特定の行からどのトークンを読み込む必要があるかを正確に定義できます。パターンに一致しない行は無視されます。regular expression

2 つのアプローチを説明するために、私の研究コンテキストからの例を使用します。
多くの場合、次の構造のファイルをロードする必要があります。

6
 generated by VMD
  CM         5.420501        3.880814        6.988216
  HM1        5.645992        2.839786        7.044024
  HM2        5.707437        4.336298        7.926170
  HM3        4.279596        4.059821        7.029471
  OD1        3.587806        6.069084        8.018103
  OD2        4.504519        4.977242        9.709150
6
 generated by VMD
  CM         5.421396        3.878586        6.989128
  HM1        5.639769        2.841884        7.045364
  HM2        5.707584        4.343513        7.928119
  HM3        4.277448        4.057222        7.022429
  OD1        3.588119        6.069086        8.017814

これらのファイルは巨大 (GB) になる可能性があり、数値データのみに関心があります。すべてのデータ ブロックは同じサイズ6(この例では) であり、常に 2 行で区切られています。したがって、strideブロックの は8です。

最初のアプローチの使用:

まず、不要な行を除外するジェネレーターを定義します。

def filter_lines(f, stride):
    for i, line in enumerate(f):
        if i%stride and (i-1)%stride:
            yield line

次に、ファイルを開き、filter_lines-generator を作成し (ここでは を知る必要がありますstride)、そのジェネレータを に渡しgenfromtxtます。

with open(fname) as f:
    data = np.genfromtxt(filter_lines(f, 8),
                         dtype='f',
                         usecols=(1, 2, 3))

これは簡単に機能します。usecolsデータの最初の列を取り除くために使用できることに注意してください。genfromtxt同様に、型の検出、列ごとの型の変化、欠損値、コンバーターなどの他のすべての機能を使用できます。

この例data.shapeでは(204000, 3)、元のファイルが272000行で構成されていました。

ここでgeneratorは、均一にまたがった行をフィルタリングするために使用されますが、(単純な) 基準に基づいて不均一な行のブロックを除外することも同様に想像できます。

2 番目のアプローチを使用する:

regexp使用するのは次のとおりです。

regexp = r'\s+\w+' + r'\s+([-.0-9]+)' * 3 + r'\s*\n'

グループ (内部()) は、特定の行から抽出されるトークンを定義します。次に、fromregexジョブを実行し、パターンに一致しない行を無視します。

data = np.fromregex(fname, regexp, dtype='f')

結果は、最初のアプローチとまったく同じです。

于 2013-02-09T20:29:38.577 に答える
1

タイプのリスト(フォーマット条件)を渡し、tryブロックを使用し、yieldを使用してgenfromtxtをジェネレーターとして使用すると、を複製できるはずtextscan()です。

def genfromtext(fname, formatTypes):
    with open(fname, 'r') as file:
        for line in file:
            try:
                line = line.split(',')  # Do you care about line anymore?
                r = []
                for type, cell in zip(formatTypes, line):
                    r.append(type(cell))
            except:
                pass  # Fail silently on this line since we hit an error
            yield r

編集:私は例外ブロックを忘れました。これで問題なく実行され、genfromtextをジェネレーターとして使用できます(私が座っているランダムなCSVログを使用)。

>>> a = genfromtext('log.txt', [str, str, str, int])
>>> a.next()
['10.10.9.45', ' 2013/01/17 16:29:26', '00:00:36', 0]
>>> a.next()
['10.10.9.45', ' 2013/01/17 16:22:20', '00:08:14', 0]
>>> a.next()
['10.10.9.45', ' 2013/01/17 16:31:05', '00:00:11', 3]

おそらくzip、コンマ分割行とformatSpecを一緒に圧縮して、2つのリストをまとめて(リストの1つがアイテムを使い果たしたときに停止する)、それらを一緒に反復できるようにして、ループに依存するループを回避していることに注意してください。len(line)またはそのようなもの。

于 2013-02-01T12:49:21.673 に答える
0

OPへのコメントを実演しようとしています。

def fread(name, cond):
    with open(name) as file:
        for line in file:
            if cond(line):
                yield line.split()

def a_genfromtxt_cond(fname, cond=(lambda str: True)):
    """Seems to work without need to convert to float."""
    return np.array(list(fread(fname, cond)), dtype=np.float64)

def b_genfromtxt_cond(fname, cond=(lambda str: True)):
    r = [[int(float(i)) for i in l] for l in fread(fname, cond)]
    return np.array(r, dtype=np.integer)


a = a_genfromtxt_cond("tar.data")
print a
aa = b_genfromtxt_cond("tar.data")
print aa

出力

[[ 1.   2.3  4.5]
 [ 4.7  9.2  6.7]
 [ 4.7  1.8  4.3]]
[[1 2 4]
 [4 9 6]
 [4 1 4]]
于 2013-02-01T14:52:29.623 に答える