Oliver Oliver
Oliver Oliver

Reputation: 2317

R - substring phrase in middle of string

I want to substring out a phrase that's in the middle of a string in R. The string is a field in a data frame. It data frame looks something like this:

Common.name price description
Animal 1    $50   Field Collected\nRoughly 2-3 Inches In Length\nVibrant Red Coloration\nWill Do Fine In Groups\nFeeding On Various Vegetation & Fruits\nSizes Range From 1-2.5 Feet In Total Length\nField Collected\nSizes Vary From Juvenile ...
Animal 2    $40   Captive Bred\nApproximately 10-12 Inches In Length\nBeautiful Hybrid to Add To Your Collection\nInsane Patterning And Beautiful Vibrant Colors\nMales And Females...
...    ...  ...   ...
Animal 500  $29   Field Collected Beauties\nMales And Females Available\nApproximate Length: no bigger than a penny\nAmazingly Friendly! Make Great Pets!\nOnly Reach About 9 Inches At Most!\nFeeding On Vitamin Dusted Greens And ...

I want to extract the length and units in each description as new fields. For Animal 1 it would be 2-3 in one field and inches in another field. For Animal 500 the length would be "no bigger than a penny" and the unit field would be NA.

How can I do this in R?

Upvotes: 0

Views: 468

Answers (1)

Ro Yo Mi
Ro Yo Mi

Reputation: 14990

Description

This Regex will do the following:

  • Match line starting with Animal followed by a number
  • capture the Animal number
  • find the first field with the word length somewhere in the field
  • if the length is expressed as numbers
    • capture the length as a single number 234 or a range of numbers 3-342
    • assume the string after the numbers is the unit of measure
  • if the length is expressed as some weird text
    • capture everything after the :
    • leave the UnitOfMeasure as null

The Regex

^(?<Animal>Animal\s[0-9]+)\s+\S+\s+(?:(?:(?!\\n|$).)*\\n)*?(?=(?:(?!\\n).)*Length)(?:(?:(?!\\n).)*?(?<Length>[0-9]+\s*(?:-\s*[0-9]+)?)\s+(?<UnitOfMeasure>\S+)|(?:(?!\\n).)*?Length:\s*(?<Length>(?:(?!\\n).)*))?

Regular expression visualization

Notes

  • I used the following flags: multiline, global, and allow duplicate subpattern names
  • I wasn't sure if the \n strings in your source text were literally \n or if they represented return characters. So this regex is constructed to assume they are literally a \ character followed by a n character. If you meant these characters to represent a new line character, then change all the \\n to \n in the regex

Examples

Live example

https://regex101.com/r/nL1fW1/2

Sample input text

Common.name price description
Animal 1    $50   Field Collected\nRoughly 2-3 Inches In Length\nVibrant Red Coloration\nWill Do Fine In Groups\nFeeding On Various Vegetation & Fruits\nSizes Range From 1-2.5 Feet In Total Length\nField Collected\nSizes Vary From Juvenile ...
Animal 2    $40   Captive Bred\nApproximately 10-12 Inches In Length\nBeautiful Hybrid to Add To Your Collection\nInsane Patterning And Beautiful Vibrant Colors\nMales And Females...
Animal 3    $40   Captive Bred\nApproximately 10 Inches In Length\nBeautiful Hybrid to Add To Your Collection\nInsane Patterning And Beautiful Vibrant Colors\nMales And Females...
...    ...  ...   ...
Animal 500  $29   Field Collected Beauties\nMales And Females Available\nApproximate Length: no bigger than a penny\nAmazingly Friendly! Make Great Pets!\nOnly Reach About 9 Inches At Most!\nFeeding On Vitamin Dusted Greens And ...

Sample Matches

[0][0] = Animal 1    $50   Field Collected\nRoughly 2-3 Inches
[0][Animal] = Animal 1
[0][Length] = 2-3
[0][UnitOfMeasure] = Inches

[1][0] = Animal 2    $40   Captive Bred\nApproximately 10-12 Inches
[1][Animal] = Animal 2
[1][Length] = 10-12
[1][UnitOfMeasure] = Inches

[2][0] = Animal 3    $40   Captive Bred\nApproximately 10 Inches
[2][Animal] = Animal 3
[2][Length] = 10
[2][UnitOfMeasure] = Inches

[3][0] = Animal 500  $29   Field Collected Beauties\nMales And Females Available\nApproximate Length: no bigger than a penny
[3][Animal] = Animal 500
[3][UnitOfMeasure] = 
[3][Length] = no bigger than a penny

Explanation

This was copied from the explanation field in the live link above.

^ assert position at start of a line
(?<Animal>Animal\s[0-9]+) Named capturing group Animal
Animal matches the characters Animal literally (case sensitive)
\s match any white space character [\r\n\t\f ]
[0-9]+ match a single character present in the list below
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
0-9 a single character in the range between 0 and 9
\s+ match any white space character [\r\n\t\f ]
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
\S+ match any non-white space character [^\r\n\t\f ]
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
\s+ match any white space character [\r\n\t\f ]
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
(?:(?:(?!\\n|$).)*\\n)*? Non-capturing group
Quantifier: *? Between zero and unlimited times, as few times as possible, expanding as needed [lazy]
(?:(?!\\n|$).)* Non-capturing group
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
(?!\\n|$) Negative Lookahead - Assert that it is impossible to match the regex below
1st Alternative: \\n
\\ matches the character \ literally
n matches the character n literally (case sensitive)
2nd Alternative: $
$ assert position at end of a line
. matches any character (except newline)
\\ matches the character \ literally
n matches the character n literally (case sensitive)
(?=(?:(?!\\n).)*Length) Positive Lookahead - Assert that the regex below can be matched
(?:(?!\\n).)* Non-capturing group
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
(?!\\n) Negative Lookahead - Assert that it is impossible to match the regex below
\\ matches the character \ literally
n matches the character n literally (case sensitive)
. matches any character (except newline)
Length matches the characters Length literally (case sensitive)
(?:(?:(?!\\n).)*?(?<Length>[0-9]+\s*(?:-\s*[0-9]+))\s+(?<UnitOfMeasure>\S+)|(?:(?!\\n).)*?Length:\s*(?<Length>(?:(?!\\n).)*))? Non-capturing group
Quantifier: ? Between zero and one time, as many times as possible, giving back as needed [greedy]
1st Alternative: (?:(?!\\n).)*?(?<Length>[0-9]+\s*(?:-\s*[0-9]+))\s+(?<UnitOfMeasure>\S+)
(?:(?!\\n).)*? Non-capturing group
Quantifier: *? Between zero and unlimited times, as few times as possible, expanding as needed [lazy]
(?!\\n) Negative Lookahead - Assert that it is impossible to match the regex below
\\ matches the character \ literally
n matches the character n literally (case sensitive)
. matches any character (except newline)
(?<Length>[0-9]+\s*(?:-\s*[0-9]+)?) Named capturing group Length
[0-9]+ match a single character present in the list below
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
0-9 a single character in the range between 0 and 9
\s* match any white space character [\r\n\t\f ]
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
(?:-\s*[0-9]+)? Non-capturing group
- matches the character - literally
\s* match any white space character [\r\n\t\f ]
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
[0-9]+ match a single character present in the list below
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
0-9 a single character in the range between 0 and 9
\s+ match any white space character [\r\n\t\f ]
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
(?<UnitOfMeasure>\S+) Named capturing group UnitOfMeasure
\S+ match any non-white space character [^\r\n\t\f ]
Quantifier: + Between one and unlimited times, as many times as possible, giving back as needed [greedy]
2nd Alternative: (?:(?!\\n).)*?Length:\s*(?<Length>(?:(?!\\n).)*)
(?:(?!\\n).)*? Non-capturing group
Quantifier: *? Between zero and unlimited times, as few times as possible, expanding as needed [lazy]
(?!\\n) Negative Lookahead - Assert that it is impossible to match the regex below
\\ matches the character \ literally
n matches the character n literally (case sensitive)
. matches any character (except newline)
Length: matches the characters Length: literally (case sensitive)
\s* match any white space character [\r\n\t\f ]
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
(?<Length>(?:(?!\\n).)*) Named capturing group Length
(?:(?!\\n).)* Non-capturing group
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
(?!\\n) Negative Lookahead - Assert that it is impossible to match the regex below
\\ matches the character \ literally
n matches the character n literally (case sensitive)
. matches any character (except newline)
m modifier: multi-line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
g modifier: global. All matches (don't return on first match)
J modifier: Allow duplicate subpattern names

Upvotes: 1

Related Questions