3

生物学的シーケンスの位置の特定の構文を解析しようとしています。位置には次のような形式があります。

12           -- a simple position in the sequence
12+34        -- a complex position as a base (12) and offset(+34)
12_56        -- a range, from 12 to 56
12+34_56-78  -- a range as a start to end, where either or both may be simple or complex

大まかに次のように、これらを辞書として解析したいと思います。

12          -> { 'start': { 'base': 12, 'offset': 0 },  'end': None }
12+34       -> { 'start': { 'base': 12, 'offset': 34 }, 'end': None }
12_56       -> { 'start': { 'base': 12, 'offset': 0 },
                   'end': { 'base': 56, 'offset': 0 } }
12+34_56-78 -> { 'start': { 'base': 12, 'offset': 0 }, 
                   'end': { 'base': 56, 'offset': -78 } }

pyparsing を使用していくつかの刺し傷を作成しました。ここに1つあります:

from pyparsing import *
integer = Word(nums)
signed_integer = Word('+-', nums)
underscore = Suppress('_')
position = integer.setResultsName('base') + Or(signed_integer,Empty).setResultsName('offset')
interval = position.setResultsName('start') + Or(underscore + position,Empty).setResultsName('end')

結果は私が望むものに近いです:

In [20]: hgvspyparsing.interval.parseString('12-34_56+78').asDict()
Out[20]: 
{'base': '56',
'end': (['56', '+78'], {'base': [('56', 0)], 'offset': [((['+78'], {}), 1)]}),
'offset': (['+78'], {}),
'start': (['12', '-34'], {'base': [('12', 0)], 'offset': [((['-34'], {}), 1)]})}

2 つの質問:

  1. asDict() は、ルートの parseResult でのみ機能しました。ネストされた dict を返すように pyparsing を甘やかす方法はありますか (そしてそれだけです)?

  2. 範囲の終わりと位置のオフセットのオプションを取得するにはどうすればよいですか? 位置ルールの Or() はそれをカットしません。(範囲の終わりについても同様に試しました。) 理想的には、すべての位置を最も複雑な形式 (つまり、{ start: {base, end}, end: { base, end } }) の特殊なケースとして扱います。単純なケースでは 0 または None を使用します。)

ありがとう!

4

2 に答える 2

3

一般的な pyparse のヒント:

Or(expr, empty)のように書いたほうがよいOptional(expr)。また、Or 式はクラス Empty で Or を作成しようとしていました。おそらく、2 番目の引数にEmpty()orを書くつもりでした。empty

expr.setResultsName("name")次のように記述できるようになりましたexpr("name")

結果に構造を適用する場合は、 を使用しますGroup

解析結果の構造をよりよく表示するには、dump()代わりに使用します。asDict()

これが私があなたの表現を構築する方法です:

from pyparsing import Word, nums, oneOf, Combine, Group, Optional

integer = Word(nums)

sign = oneOf("+ -")
signedInteger = Combine(sign + integer)

integerExpr = Group(integer("base") + Optional(signedInteger, default="0")("offset"))

integerRange = integerExpr("start") + Optional('_' + integerExpr("end"))


tests = """\
12
12+34
12_56
12+34_56-78""".splitlines()

for t in tests:
    result = integerRange.parseString(t)
    print t
    print result.dump()
    print result.asDict()
    print result.start.base, result.start.offset
    if result.end:
        print result.end.base, result.end.offset
    print

版画:

12
[['12', '0']]
- start: ['12', '0']
  - base: 12
  - offset: 0
{'start': (['12', '0'], {'base': [('12', 0)], 'offset': [('0', 1)]})}
12 0

12+34
[['12', '+34']]
- start: ['12', '+34']
  - base: 12
  - offset: +34
{'start': (['12', '+34'], {'base': [('12', 0)], 'offset': [('+34', 1)]})}
12 +34

12_56
[['12', '0'], '_', ['56', '0']]
- end: ['56', '0']
  - base: 56
  - offset: 0
- start: ['12', '0']
  - base: 12
  - offset: 0
{'start': (['12', '0'], {'base': [('12', 0)], 'offset': [('0', 1)]}), 'end': (['56', '0'], {'base': [('56', 0)], 'offset': [('0', 1)]})}
12 0
56 0

12+34_56-78
[['12', '+34'], '_', ['56', '-78']]
- end: ['56', '-78']
  - base: 56
  - offset: -78
- start: ['12', '+34']
  - base: 12
  - offset: +34
{'start': (['12', '+34'], {'base': [('12', 0)], 'offset': [('+34', 1)]}), 'end': (['56', '-78'], {'base': [('56', 0)], 'offset': [('-78', 1)]})}
12 +34
56 -78
于 2013-10-11T06:00:30.600 に答える
1

実際の構文は例よりも複雑ですか? 構文解析は純粋な Python でかなり簡単に実行できるため、次のようになります。

bases = ["12", "12+34", "12_56", "12+34", "12+34_56-78"]

def parse_base(base_string):

    def parse_single(s):
        if '-' in s:
            offset_start = s.find("-")
            base, offset = int(s[:offset_start]), int(s[offset_start:])
        elif '+' in s:
            offset_start = s.find("+")
            base, offset = int(s[:offset_start]), int(s[offset_start:])
        else:
            base = int(s)
            offset = 0
        return {'base': base, 'offset': offset}

    range_split = base_string.split('_')
    if len(range_split) == 1:
        start = range_split[0]
        return {'start': parse_single(start), 'end': None}
    elif len(range_split) == 2:
        start, end = range_split
        return {'start': parse_single(start),
                'end': parse_single(end)}

出力:

for b in bases:
     print(parse_base(b))

{'start': {'base': 12, 'offset': 0}, 'end': None}
{'start': {'base': 12, 'offset': 34}, 'end': None}
{'start': {'base': 12, 'offset': 0}, 'end': {'base': 56, 'offset': 0}}
{'start': {'base': 12, 'offset': 34}, 'end': None}
{'start': {'base': 12, 'offset': 34}, 'end': {'base': 56, 'offset': -78}}
于 2013-10-11T05:57:00.023 に答える