[英]Parsing chemical formulas using regex expressions
I'm trying to do the following thing: given a single-column pandas.Dataframe
(of chemical formulas) like我正在尝试做以下事情:给定单列
pandas.Dataframe
(化学式),例如
formula
0 Hg0.7Cd0.3Te
1 CuBr
2 Lu
...
I would like to return a pandas.Series
like我想退回
pandas.Series
类的
0 [(Hg, 0.7), (Cd, 0.3), (Te,1)]
1 [(Cu, 1), (Br, 1)]
2 [(Lu, 1), (P, 1)]
...
So this is the desired output.所以这是所需的 output。
I've already tried something with a regex expression:我已经用正则表达式尝试了一些东西:
counts = pd.Series(formulae.values.flatten()).str.findall(r"([a-z]+)([0-9]+)", re.I)
but unfortunately my output is the following:但不幸的是我的 output 如下:
0 [(Hg, 0), (Cd, 0)]
1 []
2 []
3 [(Cu, 3), (SbSe, 4)]
so it's not recognizing in some cases different elements in the chemical formula.所以它在某些情况下无法识别化学式中的不同元素。
There are a few things to be improved:有几点需要改进:
([0-9]+(?:[.][0-9]+)?)
instead.([0-9]+(?:[.][0-9]+)?)
代替。?
?
. [AZ][az]*
.[AZ][az]*
。 That's important to distinguish different elements with no number in between, eg 'CuBr'
(so ignore-case wouldn't work here).'CuBr'
(因此忽略大小写在这里不起作用)。 Putting it all together:把它们放在一起:
from pprint import pprint
import re
formulae = ['Hg0.7Cd0.3Te', 'CuBr', 'Lu']
pattern = re.compile('([A-Z][a-z]*)([0-9]+(?:[.][0-9]+)?)?')
pprint([pattern.findall(f) for f in formulae])
The prints the following:打印以下内容:
[[('Hg', '0.7'), ('Cd', '0.3'), ('Te', '')],
[('Cu', ''), ('Br', '')],
[('Lu', '')]]
As you can see, missing numbers are denoted by empty strings which you need to postprocess manually.如您所见,缺少的数字由您需要手动后处理的空字符串表示。 For example:
例如:
result = [pattern.findall(f) for f in formulae]
result = [[(e, float(n or 1)) for e, n in f] for f in result]
You can use您可以使用
import pandas as pd
df = pd.DataFrame({'formula':['Hg0.7Cd0.3Te', 'CuBr', 'Lu']})
df['counts'] = df['formula'].str.findall(r'([A-Z][a-z]*)(\d+(?:\.\d+)?)?')
df['counts'] = df['counts'].apply(lambda x: [(a,b) if b else (a,1) for a,b in x])
Output: Output:
>>> df['counts']
0 [(Hg, 0.7), (Cd, 0.3), (Te, 1)]
1 [(Cu, 1), (Br, 1)]
2 [(Lu, 1)]
Details :详情:
([AZ][az]*)
- Group 1: an uppercase letter followed with zero or more lowercase letters ([AZ][az]*)
- 第 1 组:一个大写字母后跟零个或多个小写字母(\d+(?:\.\d+)?)?
- an optional group 2: one or more diits followed with an optional occurrence of a dot and one or more digits. The df['counts'].apply(lambda x: [(a,b) if b else (a,1) for a,b in x])
adds 1
as each tuple second item where it is empty. df['counts'].apply(lambda x: [(a,b) if b else (a,1) for a,b in x])
将1
作为每个元组的第二项添加 1,如果它是空的。
Would use multiple replace to introduce separators, split using introduced separators, explode and then filter.将使用多个替换来引入分隔符,使用引入的分隔符进行拆分,分解然后过滤。 Code below
下面的代码
repl2 = lambda g: f'{str(g.group(1)) }<'
repl3 = lambda g: f'{str(g.group(1)) }>'
df1 = (df1.assign(formula1=df1['formula'].str.replace('((?<=[A-Z])\w)', repl3, regex=True)#Introduce separator where alpha numeric follows a cap letter
.str.replace('(\d(?=[A-Z]))', repl2, regex=True))#Introduce separator where digits is followed by cap letter
.replace(regex={r'\>(?=0)': ',', '\>': ',1 '})#Replace the < and > introduced separators
)
df1=df1.assign(formula1=df1['formula1'].str.split('\<|\s')).explode('formula1')#Explode dataframe
new=df1[df1['formula1'].str.contains('\w')]#filter those rows that have details
formula formula1
0 Hg0.7Cd0.3Te Hg,0.7
0 Hg0.7Cd0.3Te Cd,0.3
0 Hg0.7Cd0.3Te Te,1
1 CuBr Cu,1
1 CuBr Br,1
2 Lu Lu,1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.