Assuming:
- You know there is a comma in the string, so you don't have to search the entire string to find out if there is or not.
- You know the pattern is
'many_not_digits,few_digits'so there is a big imbalance between the size of the left/right parts either side of the comma. - You can get to the end of the string without walking it, which you can in Python because string indexing is constant time
Then you could start from the end and walk backwards looking for the comma, which would be less overall work for your examples than walking from the left looking for the comma.
Doing work in Python code is way slower than using Python engine code written in C, right? So would it really be faster?
- Make a string "aaaaa....,12"
- use the
timeitmodule to compare each approach - split, or right-walk. - Timeit does a million runs of some code.
- Extend the length of "aaaaaaaaaaaaaaaa....,12" to make it extreme.
How do they compare?
- String split: 1400 "a"'s run a million times took 1 second.
- String split: 4000 "a"'s run a million times took 2 seconds.
- Right walk: 1400 "a"'s run a million times took 0.4 seconds.
- Right walk: 999,999 "a"'s run a million times took ... 0.4 seconds.
!
from timeit import timeit
_split = """num = x.split(',')[-1]"""
_rwalk = """
i=-1
while x[i] != ',':
i-=1
num = x[i+1:]
"""
print(timeit(_split, setup='x="a"*1400 + ",12"'))
print(timeit(_rwalk, setup='x="a"*999999 + ",12"'))
e.g.
1.0063155219977489 # "aaa...,12" for 1400 chars, string split
0.4027107510046335 # "aaa...,12" for 999999 chars, rwalked. Faster.
Try it online at repl.it
I don't think this is algorithmically better than O(n), but with the constraints of the assumptions I made you have more knowledge than str.split() has, and can leverage that to skip walking most of the string and beat it in practise - and the longer the text part, and shorter the digit part, the more you benefit.
Answer from TessellatingHeckler on Stack OverflowAssuming:
- You know there is a comma in the string, so you don't have to search the entire string to find out if there is or not.
- You know the pattern is
'many_not_digits,few_digits'so there is a big imbalance between the size of the left/right parts either side of the comma. - You can get to the end of the string without walking it, which you can in Python because string indexing is constant time
Then you could start from the end and walk backwards looking for the comma, which would be less overall work for your examples than walking from the left looking for the comma.
Doing work in Python code is way slower than using Python engine code written in C, right? So would it really be faster?
- Make a string "aaaaa....,12"
- use the
timeitmodule to compare each approach - split, or right-walk. - Timeit does a million runs of some code.
- Extend the length of "aaaaaaaaaaaaaaaa....,12" to make it extreme.
How do they compare?
- String split: 1400 "a"'s run a million times took 1 second.
- String split: 4000 "a"'s run a million times took 2 seconds.
- Right walk: 1400 "a"'s run a million times took 0.4 seconds.
- Right walk: 999,999 "a"'s run a million times took ... 0.4 seconds.
!
from timeit import timeit
_split = """num = x.split(',')[-1]"""
_rwalk = """
i=-1
while x[i] != ',':
i-=1
num = x[i+1:]
"""
print(timeit(_split, setup='x="a"*1400 + ",12"'))
print(timeit(_rwalk, setup='x="a"*999999 + ",12"'))
e.g.
1.0063155219977489 # "aaa...,12" for 1400 chars, string split
0.4027107510046335 # "aaa...,12" for 999999 chars, rwalked. Faster.
Try it online at repl.it
I don't think this is algorithmically better than O(n), but with the constraints of the assumptions I made you have more knowledge than str.split() has, and can leverage that to skip walking most of the string and beat it in practise - and the longer the text part, and shorter the digit part, the more you benefit.
If you are worried about using split from the left because of lots of unwanted characters in the beginning, use rsplit.
s = "abcdef,12"
s.rsplit(",", 1)[-1]
Here, rsplit will start splitting the string from the right and the optional second argument we used will stop rsplit to proceed further than the first comma operator it encountered.
(eg):
s = "abc,def,12"
s.rsplit(",", 1)[-1]
# Outputs 12
s = "abcdef12"
s.rsplit(",", 1)[-1]
# Outputs abcdef12
This will be lot simpler and cleaner to get the string of numbers in the end rather than doing anything manually.
Not to mention, it will be lot easier if we wish to check whether we get only numbers with this. Even if it is a list of strings.
def get_numbers(string_list, skip_on_error=True):
numbers_list = []
for input_string in string_list:
the_number = input_string.rsplit(",", 1)[-1]
if the_number.isdigit():
numbers_list.append(the_number)
elif skip_on_error:
numbers_list.append("")
else:
raise Exception("Wrong Format occurred: %s" % (input_string))
return numbers_list
And if you are looking for even further optimization and sure that most(if not all) strings will be of the correct format, you can even use try except if you are going to go with an integer list instead of string list. Like this:
# Instead of the if.. elif.. else construct
try:
numbers_list.append(int(the_number))
except ValueError:
if skip_on_error:
numbers_list.append(0)
else:
raise Exception("Wrong Format occurred: %s" % (input_string))
But always remember the Zen Of Python and using split/rsplit follows these:
- Beautiful is better than ugly
- Explicit is better than implicit
- Simple is better than complex
- Readability counts
- There should be one-- and preferably only one --obvious way to do it
And also remember Donald Knuth:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%
Regular expressions to the rescue!
I see you are already on the right path, by using a regular expression. But you could try using a regular expression with a capture in order to capture the digits following the string "Episode".
Here is a small example to get you going:
import re
m = re.search('Episode (\d+)', 'series[Episode 37]-03th_July_2010-YouTube', re.IGNORECASE)
m.group(1)
The last statement, m.group(1), returns the contents of the first group (what's inside the parentheses in the regular expression). In this case it will be the string '37', which is the digits that follows the string "Episode ".
Also notice I'm using the IGNORECASE flag, so this will work regardless of the casing of the string "Episode". So "episode" and even "ePISODE" will work too.
Kind of broad, pulling from a text file, but will do for many applications of getting both integers and floats out of strings.
The file, commaseperated.txt has a row looking like this:
Input: (0, 1, 2.4), 1, 5, 8, 99.7, 0.1), (, )()!@!#, 9
import re
# a function to pull the numbers from many lists of strings
def func(a, z, c):
# -- Read .txt -- #
filename = 'commaseperated.txt'
f1 = open(filename, 'r') # open the file for reading
data = f1.readlines() # read the entire file as a list of strings
f1.close()
#
for line in data:
cells = line.strip().split(',')
# empty string
b = ""
# range through cells of from a text file split by commas
for i in range(a, len(cells[z:c])+a):
# no commas in the cells so make dummy commas for new string
b += cells[i] + ','
# find all the numbers
z = re.findall(r'\d+(?:\.\d+)?', b)
# return a list of floated values from the text string
return [float(z[s]) for s in range(len(z))]
Output: [0, 1, 2.4, 1, 5, 8, 99.7, 0.1, 9]
How to extract numbers within a string?
You want to look into regular expressions (regex) and Python's re module.
import re
string = "CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16"
matches = re.findall("(\d+)", string)
print(matches)
Output: ['16', '2', '8', '4', '3200', '16']
The regex pattern \d+ matches 1 or more numerical characters in sequence. By putting it in brackets ( ) it captures each occurrence of that pattern as a group. And the re.findall method returns those groups in a list. Note that they are still strings, not numbers.
How to extract numbers from a string in Python? - Stack Overflow
python - How to extract number from end of the string - Stack Overflow
I'm trying to extract a specific string of numbers that is between letters, sometimes followed by more numbers. How do I get just that particular string of numbers? (example below)
Videos
You're looking for str.extract with a lookbehind on the character 'q'.
df['AmountPaid'] = df.Product.str.extract(
r'(?<=q)(\d+)', expand=False
).fillna(0).astype(int)
df
Product AmountPaid
0 none 0
1 q1 1
2 q123 123
3 q12_a123 12
In complement to cs95's answer, since str.extract only captures what is inside capturing groups, a lookbehind is not needed.
It's possible to use directly q(\d+):
df['AmountPaid'] = (df['Product'].str.extract(r'q(\d+)', expand=False)
.fillna(0).astype(int)
)
Output:
Product AmountPaid
0 none 0
1 q1 1
2 q123 123
3 q12_a123 12
This is not just shorter in terms of syntax but also more efficient:
- regex demo with lookbehind
- regex demo without lookbehind
Hi I am new to Python but am working on a project where I want to extract numbers from a string. For example I have the string "CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16" where I want to extract the "16" from "16GB" as well as the "2" and "8" in "(2x8GB)" and "3200" from "3200MHZ".
What is the best way to do this? Thanks in advance!
Edit: Thanks everyone for the help! Regex seems to help do the trick
You want to look into regular expressions (regex) and Python's re module.
import re
string = "CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16"
matches = re.findall("(\d+)", string)
print(matches)
Output: ['16', '2', '8', '4', '3200', '16']
The regex pattern \d+ matches 1 or more numerical characters in sequence. By putting it in brackets ( ) it captures each occurrence of that pattern as a group. And the re.findall method returns those groups in a list. Note that they are still strings, not numbers.
You can use regex:
>>> import re
>>> re.findall('[0-9]+', 'CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16')
['16', '2', '8', '4', '3200', '16']
I'd use a regexp:
>>> import re
>>> re.findall(r'\d+', "hello 42 I'm a 32 string 30")
['42', '32', '30']
This would also match 42 from bla42bla. If you only want numbers delimited by word boundaries (space, period, comma), you can use \b:
>>> re.findall(r'\b\d+\b', "he33llo 42 I'm a 32 string 30")
['42', '32', '30']
To end up with a list of numbers instead of a list of strings:
>>> [int(s) for s in re.findall(r'\b\d+\b', "he33llo 42 I'm a 32 string 30")]
[42, 32, 30]
NOTE: this does not work for negative integers
If you only want to extract only positive integers, try the following:
>>> txt = "h3110 23 cat 444.4 rabbit 11 2 dog"
>>> [int(s) for s in txt.split() if s.isdigit()]
[23, 11, 2]
I would argue that this is better than the regex example because you don't need another module and it's more readable because you don't need to parse (and learn) the regex mini-language.
This will not recognize floats, negative integers, or integers in hexadecimal format. If you can't accept these limitations, jmnas's answer below will do the trick.
You don't need RegEx here, simply use the built-in str.rindex function and slicing, like this
>>> data = "Titile Something/17"
>>> data[:data.rindex("/")]
'Titile Something'
>>> data[data.rindex("/") + 1:]
'17'
Or you can use str.rpartition, like this
>>> data.rpartition('/')[0]
'Titile Something'
>>> data.rpartition('/')[2]
'17'
>>>
Note: This will get any string after the last /. Use it with caution.
If you want to make sure that the split string is actually full of numbers, you can use str.isdigit function, like this
>>> data[data.rindex("/") + 1:].isdigit()
True
>>> data.rpartition('/')[2].isdigit()
True
\d{0,3} matches from zero upto three digits. $ asserts that we are at the end of a line.
re.search(r'/\d{0,3}$', st).group()
Example:
>>> re.search(r'/\d{0,3}$', 'Titile Something/17').group()
'/17'
>>> re.search(r'/\d{0,3}$', 'Titile Something/1').group()
'/1'
This is such a simple question, I almost feel dumb asking it. I feel like this is a basic day one "index through a string assignment" but I can't quite get it right.
I'm going through old g-code files to extract data. Python is reading the g-code as if it were a string, and I'm making patterns to rip data out of the file and store it in a dictionary. I can't quite seem to get the pattern down for this one
So I have a string like the following:
G0Z1.210T1500000
the part I want to extract is G0Z1.210T1500000
I don't want the G0Z1.210T1500000
I only want the numbers that follow the "G0Z", and I want to throw out everything else. It keeps on adding the rest of the numbers to it
So here is what I have so far:
text = "G0Z1.210T1500000"
noGoz = text[3 : len(text) - 1]
Over_All_Length = ""
while True:
for isNumb in noGoz:
if isNumb.isnumeric():
Over_All_Length += isNumb
elif isNumb == ".":
Over_All_Length += isNumb
else:
break
# Sanity check:
print(Over_All_Length)
Instead of a while True I've done stuff like this:
KillSwitch = "Live"
while KillSwitch == "Live"
for isNumb in noGoz:
if isNumb.isnumeric():
Over_All_Length += isNumb
elif isNumb == ".":
Over_All_Length += isNumb
else:
KillSwitch = "Dead"
# Sanity check:
print(Over_All_Length)and this:
count = 0
if count == 0:
for isNumb in noGoz:
if isNumb.isnumeric():
Over_All_Length += isNumb
elif isNumb == ".":
Over_All_Length += isNumb
else:
count += 1
# Sanity check:
print(Over_All_Length)I've tried all sorts of tricks where basically you set a condition for a loop to be true, and then change it when you hit the first non-number or non- period character. Nothing seems to work