Разобрать все подстроки перед шаблоном без цикла? - PullRequest
0 голосов
/ 13 мая 2018

У меня есть длинная строка, состоящая из множества чисел, разделенных пробелами (и иногда там даже добавляется новая строка).Я хотел бы просмотреть строку и добавить все числа в новый список, которые идут до начала 0.000000000000000000e+00 чисел.Итак, вот пример моей строки:

my_string = '1.249132165057832031e+13 1.638194600635518555e+13 2.127995187558799219e+13 2.744617593148214062e+13 -2.558800658636701519e+28 5.918883595148564680e+30 3.603563681248702509e+31 4.325917213186498068e+31 4.911908042151239481e+31 4.463331378152286632e+31 3.684371076399113503e+31 2.500614504012405068e+31 9.997365425073173512e+30 -7.046725649106466938e+30 -2.192076417151744811e+31 -2.531287564917444482e+31 -6.962936418905874724e+30 3.281685507310205847e+31 9.241630178064907840e+31 1.730544785932614751e+32 2.619210949875333106e+32 2.984440142196566918e+32 8.964375812060072923e+31 -8.515727465135046667e+32 -3.425309034394939997e+33 -8.145884847188906515e+33 -9.922370830834364410e+33 -2.119464668318252366e+28 -1.689726703118075140e+27 1.440101653069986610e+26 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.186324149659251562e+13 8.113154959294240625e+13 1.053889122977165625e+14 1.359271226298647969e+14 -2.097046363337115528e+28 4.850777756495711585e+30 2.953274256558218597e+31 3.545273642763729060e+31 4.025456872055449111e+31 3.657581460085835446e+31 3.018816679659856350e+31 2.048223110003727437e+31 8.176806147340775115e+30 -5.796250740354887641e+30 -1.798839398031696094e+31 -2.076444435341100150e+31 -5.711669151245612857e+30 2.691583747083509247e+31 7.579958708961477309e+31 1.419395486743453834e+32 2.148287875274468622e+32 2.447859658750551118e+32 7.352862842410293685e+31 -6.984595303325589259e+32 -2.809449882735912952e+33 -6.681296633318354125e+33 -8.138406580426555140e+33 -1.740744048703962454e+28 -1.411749034480591280e+27 8.079362883576220633e+25 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00'

и из этой строки все, что я хочу в итоге, будет:

new_list = ['1.440101653069986610e+26', '8.079362883576220633e+25']

Я думал, что буду использовать регулярное выражение, ноэто кажется немного сложным, так как у меня есть группа 0.000000000000000000e+00 вхождений, сгруппированных вместе, и я хочу только ненулевое число прямо перед первым нулевым вхождением.Я также не могу предположить, что всегда есть равное количество нулей, сгруппированных вместе.

Я также думал о разбиении по пробелам и итерации, но моя полная строка на самом деле слишком длинная, чтобы сделать это эффективно.Как я могу это сделать?

Ответы [ 4 ]

0 голосов
/ 13 мая 2018

Если вы хотите значения float , а не их строковые представления:

import re

list(
    filter(
        None,
        map(
            float,
            re.findall(r"\S+(?=\s0\.0+e)", my_string)
)))
  • re.findall(r"\S+(?=\s0\.0+e)", my_string):
    находит все вхождения последовательностей символов, не являющихся пробелами, перед пробелом и 0,00000… e
  • map(float, ^ ):
    предположим, что все вышеперечисленные совпадения могут быть преобразованы в число с плавающей точкой
  • filter(None, ^ ):
    отфильтровать все нулевые числа с плавающей запятой
  • list( ^ ):
    превратить все вышеперечисленное в список (неиспользование в Python 2, преобразование генератора в список в Python 3)

Результат:

>>> list(filter(None, map(float, re.findall(r"\S+(?=\s0\.0+e)", my_string))))
[1.4401016530699866e+26, 8.07936288357622e+25]

Однако, если вам все еще нужны сами строковые значения, дайте мне знать; в этом случае подвыражения map & filter необходимо изменить.

0 голосов
/ 13 мая 2018

Я также не могу предположить, что всегда есть равное количество нулей, сгруппированных вместе.

Как мы можем отличить, скажем, 2 последовательных нулевых значения от "группы нулей"?

Что ж, учитывая, что вы ищете не менее 5 0.000 шаблонов, вы можете использовать группу без захвата для этого шаблона с несколькими 0 (чтобы избежать его соответствия), следуя непустому шаблону (длячисло)

re.findall("(\S+)\s+(?:0\.0+e\+00\s+){5,}",my_string)

Если не может быть никаких нулей, кроме самого шаблона, его можно обобщить так:

re.findall("(\S+)\s+(?:0\.0+e\+00\s+)+",my_string)

(вам нужен + в концегруппа без захвата для захвата и отбрасывания всех нулей)

результат (в обоих случаях):

['1.440101653069986610e+26', '8.079362883576220633e+25']

это также заботится о новых строках и допускаетпеременное число нулей в десятичной части

0 голосов
/ 13 мая 2018

Список понимания и почтовый индекс

Это примерно в 10-70x раз быстрее, чем другие решения.

my_values = my_string.split()
output = [x for x,y in zip(my_values,my_values[1:]) 
           if (y == '0.000000000000000000e+00' and x != '0.000000000000000000e+00')]
print(output)

Или с островком, чтобы сохранить память, как любезно предложено @ Жан-Франсуа Фабр:

import itertools
my_values = my_string.split()
output = [x for x,y in zip(my_values,itertools.islice(myvalues,1,None)) 
               if (y == '0.000000000000000000e+00' and x != '0.000000000000000000e+00')]
print(output)

Это работает путем группировки элементов в пары (x, y). х должен отличаться от 0.00.., а у должен быть равен ему. Выполнив сначала проверку y, вы в большинстве случаев получите быстрое значение False и продолжите итерацию. Возвращает:

['1.440101653069986610e+26', '8.079362883576220633e+25']

Панды и NumPy

Однако другой идеей (которую я бы счел здесь наиболее разумной) было бы использование панд и pd.to_numeric(). Когда вы работаете с числами, вы, скорее всего, захотите использовать такую ​​библиотеку, как numpy или pandas. Это будет безопаснее, так как вы также можете плавно обрабатывать ошибки Также обратите внимание, что я в обоих случаях преобразовываю числа обратно в строку (которую можно пропустить).

import pandas as pd

data = pd.Series(pd.to_numeric(my_string.split()))
output = data[(data != 0) & (data.shift(-1) == 0)].astype(str).tolist()
print(output)

#['1.440101653069986610e+26', '8.079362883576220633e+25']

И NumPy:

import numpy as np

data = np.loadtxt(my_string.split())
output = list(map(str,data[(data != 0) & (np.roll(ar, -1) == 0)]))
print(output)

#['1.440101653069986610e+26', '8.079362883576220633e+25']

Сравнение времени

самый быстрый -> самый медленный

100000 loops, best of 3: 9.28 µs per loop  <-- Anton vBR list comprehension
10000 loops, best of 3: 98.4 µs per loop   <-- Revos Regex
1000 loops, best of 3: 256 µs per loop     <-- Anton vBR numpy
1000 loops, best of 3: 425 µs per loop     <-- Tzot Regex
1000 loops, best of 3: 513 µs per loop     <-- Jean-François Fabre Regex 
1000 loops, best of 3: 782 µs per loop     <-- liliscent 
1000 loops, best of 3: 794 µs per loop     <-- Anton vBR pandas
0 голосов
/ 13 мая 2018

Вы можете использовать отрицательное утверждение с обратным взглядом:

In [55]: re.findall(r'(\S+)(?<!0\.000000000000000000e\+00)\s+0\.000000000000000000e\+00', my_string)
Out[55]: ['1.440101653069986610e+26', '8.079362883576220633e+25']

Используя отрицательное утверждение с предварительным прогнозом, можно улучшить регулярное выражение для повышения производительности, как упомянуто в комментарии @revo:

([-+]?\d\.(?!0+e\+0+)\S+)\s+(?:0\.0+e\+00\s*)+

Live демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...