-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path2_regex.Rmd
More file actions
404 lines (294 loc) · 7.75 KB
/
2_regex.Rmd
File metadata and controls
404 lines (294 loc) · 7.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
---
title: |-
Python Crash Course \
Part 2 -- Intro to Regex
author: "Rob Colautti"
---
# 1. Overview
Regular Expressions
* aka 'regex' and 'regexp'
* a sort of find-and-replace for nerds
* one of the most powerful data tools I have ever learned
* requires patience and lots of **practice**
# 2. Basic Regex Functions in Python
The `re` module in Python contains functions for regular expressions
## Handy regex functions in `re`
### Searching
`re.match()` - match between _pattern_ and whole _target string_
`re.search()` - find first match of _pattern_ within _target string_
`re.findall()` - find all non-overlapping _pattern_ within _target string_
### Modifying
`re.split()` - split _target string_ according to _pattern_
`re.sub()` - replace occurrences of _pattern_ within _string_
## `re.match()`
Returns 'None' if no match
```{python}
import re
m1=re.match("salicaria","salicaria")
print(m1)
m2=re.match("a","salicaria")
print(m2)
m3=re.match("z","salicaria")
print(m3)
```
## `re.search()`
Contrast output with `re.match()`
```{python}
import re
m1=re.search("salicaria","salicaria")
print(m1)
m2=re.search("a","salicaria")
print(m2)
m3=re.search("z","salicaria")
print(m3)
```
> What is different?
## `re.findall()`
Note `[]` instead of `None` when no match is found
```{python}
import re
m1=re.findall("salicaria","salicaria")
print(m1)
m2=re.findall("a","salicaria")
print(m2)
m3=re.findall("z","salicaria")
print(m3)
```
## `re.split()`
Compare this output with previous functions
```{python}
import re
m1=re.split("salicaria","salicaria")
print(m1)
m2=re.split("a","salicaria")
print(m2)
m3=re.split("z","salicaria")
print(m3)
```
> What is different?
## `re.sub()`
For this function, we have to add a _replace string_ parameter
```{python}
import re
m1=re.sub("salicaria","X","salicaria")
print(m1)
m2=re.sub("a","X","salicaria")
print(m2)
m3=re.sub("z","X","salicaria")
print(m3)
```
# 3. Wildcards
## Escape character: `\`
The escape character tells the Python interpreter that the next string is not to be interpreted literally. Unlike regular expressions in R, python uses only a single backslash.
## `\w`
All letters and digits (aka 'words')
```{python}
from re import sub
m1=sub("w","*","...which 1-100 words get replaced?")
print(m1)
m2=sub("\w","*","...which 1-100 words get replaced?")
print(m2)
```
## `\W`
Upper case `\W` means the inverse of lower-case `\w` (non-word and non-number)
```{python}
m1=sub("\W","*","...which 1-100 words get replaced?")
print(m1)
```
## `\s`
Empty space
```{python}
m1=sub("\s","*","...which 1-100 words get replaced?")
print(m1)
```
## `\t`
Tab character (useful for tab-delimited data files)
```{python}
m1=sub("\t","*","...which 1-100 words get replaced?")
print(m1)
```
## `\d`
Digits (numbers)
```{python}
m1=sub("\d","*","...which 1-100 words get replaced?")
print(m1)
```
## `\D`
Inverse of `\d` (non-digits)
```{python}
m1=sub("\D","*","...which 1-100 words get replaced?")
print(m1)
```
## `.` = any character except new line
```{python}
m1=sub(".","*","...which 1-100 words get replaced?")
print(m1)
```
So how to search for a period? Use the escape character
```{python}
m1=sub("\.","*","...which 1-100 words get replaced?")
print(m1)
```
## __Two more special wildcards:__
### `\r`
Carriage return
### `\n`
Newline character
Unix/Mac files -- lines usually end with `\n` only
Windows/DOS files -- lines usually end with `\r\n`
> FUN FACT: Where does the term 'Carriage return' come from?
# 4. Special characters:
## Or: `|`
The vertical bar or 'pipe' is often located above the backslash (shift-backslash). It is the regex equivalent of __or__
For example, look for w __or__ e
```{python}
m1=sub("w|e","*","...which 1-100 words get replaced?")
print(m1)
```
## `*`, `?`, `+` and `{}`
Use `*`, `?`, `+` and `{}` for more complicated searches
Look at these examples carefully
```{python}
m1=sub("\w","*","...which 1-100 words get replaced?")
print(m1)
```
### `+`
1 or more occurrences
```{python}
m1=sub("\w+","*","...which 1-100 words get replaced?")
print(m1)
```
### `?`
This tells regex to do a 'lazy' search (find the first occurrance)
```{python}
m1=sub("\w?","*","...which 1-100 words get replaced?")
print(m1)
```
### `*`
This tells regex to do a 'greedy' search (find the last occurrence)
```{python}
m1=sub("\w*","*","...which 1-100 words get replaced?")
print(m1)
```
## `{n,m}`
Find between n to m matches
```{python}
m1=sub("\w{3,4}","*","...which 1-100 words get replaced?")
print(m1)
```
### `{n}` = exactly n matches
```{python}
m1=sub("\w{3}","*","...which 1-100 words get replaced?")
print(m1)
```
### `{n,}`= n or more matches
```{python}
m1=sub("\w{4,}","*","...which 1-100 words get replaced?")
print(m1)
```
# 5. Multiple search: `[]`
Use square brackets to find any matching characters.
```{python}
m1=sub("which","*","...which 1-100 words get replaced?")
print(m1)
m2=sub("[which]","*","...which 1-100 words get replaced?")
print(m2)
```
## Range of characters `-`
Use dash for a range of numbers
```{python}
m1=sub("[1-4]","*","1234567890")
print(m1)
```
or letters
```{python}
m1=sub("[a-z]","*","AaBbCcDd")
print(m1)
m2=sub("[A-Z]","*","AaBbCcDd")
print(m2)
m3=sub("[A-z]","*","AaBbCcDd")
print(m3)
```
# 6. `^`Start and end of line`$`
## `^` Start of line
Find species starting with "s"
```{python}
m1=sub("^s","*","start of lines")
print(m1)
```
## `[^]`
IMPORTANT: ^ Also 'negates' when used within []
Find species containing any letter other than s
```{r}
m1=sub("[^s]","*","start of lines")
print(m1)
```
## $ End of line
Find species ending with "a"
```{python}
m1=sub("s$","*","start of lines")
print(m1)
```
# 7. Capture text: `()`
Capture text using `()` and reprint using `\\1`, `\\2`, etc
Replace each word with its first letter
```{python}
m1=sub(".*(\w\w+).*","\\1","...which 1-100 words get replaced?")
print(m1)
```
Pull out only the numbers and reverse their order
```{python}
m1=sub(".*([0-9]+)-([0-9]+).*","\\2-\\1","...which 1-100 words get replaced?")
print(m1)
```
Reverse first two letters of each 'word' containing 3+ characters
```{python}
m1=sub("(\w)(\w)(\w+)","\\2\\1\\3","...which 1-100 words get replaced?")
print(m1)
```
## `group()` - find matching group
As you can see above, `re.match()` and `re.search()` return an object if there is a match. What if we want to see what was matched? Use `.group()`
```{python}
import re
m1=re.match("salicaria","salicaria")
print(m1.group())
```
But group() produces an error if no match was generated:
```{python, error=T}
import re
m2=re.match("z","salicaria")
print(m2)
print(m1.group())
```
## `groups()` - find matching subgroups
Brackets can be used to subset searches, and then `groups()` identifies the matched groups.
```{python}
import re
m1=re.match("sal","salicaria")
print(m1)
print(m1.groups())
m2=re.match("(s)(a)(l)","salicaria")
print(m2)
print(m2.groups())
print(m2.group(1))
print(m2.group(2))
print(m2.group(3))
```
# PRACTICE EXERCISES
## 1. Consider a vector of email addresses scraped from the internet:
* robert 'dot' colautti 'at' queensu 'dot' ca
* chris.eckert[at]queensu.ca
* lonnie.aarssen at queensu.ca
Use regular expressions to convert all email addresses to the standard format: name@queensu.ca
## 2. Create a random sequence of DNA:
```{python}
MySeq="ATGTGTGATAGATATAGTTTATAG"
```
* Replace T with U
* Find all start codons (AUG) and stop codons (UAA, UAG, UGA)
* Find all open reading frames (hint: consider each sequence beginning with AUG and ending with a stop codon; how do you know if both sequences are in the same reading frame?)
* Count the length of bp for all open reading frames
## 3. More online examples
[http://regex.sketchengine.co.uk/extra_regexps.html](http://regex.sketchengine.co.uk/extra_regexps.html)
## 4. Regex Golf
Have fun! [LINK](https://alf.nu/RegexGolf)