替换 pandas 数据框中的字符串
Substitute string in pandas dataframe
我有一个 pandas.DataFrame
,其中包含说明酶是否表达的布尔规则。有些规则很简单(表达取决于 1 个基因),有些规则更复杂(表达取决于几个基因)
>>gprs.head()
Out[362]:
Rxn rule
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80
6 24_25VITD2Hm HGNC:2602
8 25VITD2Hm HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251) or (HGNC:250 and HGNC:251) or HGNC:252 or HGNC:253 or HGNC:255 or HGNC:256
...
一个包含基因表达信息的字典对象:(1=expr, 0=not expr)
>>translation
'HGNC:80':1
'HGNC:2602':0
etc...
我想将 'translation' 对象中包含的表达式信息替换到我的 'gprs' pandas.DataFrame
中。到目前为止我有:
for index, row in gprs.iterrows():
row['rule']=row['rule'].replace(r'(', "")
row['rule']=row['rule'].replace(r')', "")
ruleGenes=re.split(" and | or ",(row['rule']))
for gene in ruleGenes:
if re.match("HGNC:HGNC:", gene):
gene=gene[5:]
try:
gprs=gprs.replace(gene,translation[gene])
except:
print 'error in ', gene
else:
try:
gprs=gprs.replace(gene,translation[gene])
except:
print 'error in ', gene
这仅在规则很简单(1 个元素)但对更复杂的规则无效时有效:
>>gprs.head()
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80
6 24_25VITD2Hm 0
7 24_25VITD3Hm HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251) or (HGNC:250 and HGNC:251) or HGNC:252 or HGNC:253 or HGNC:255 or HGNC:256
最终我想用 max() 函数替换 'or',用 min() 函数替换 'and' 并评估布尔规则。
有什么建议吗?
编辑:
使用 EFT 代码时,当一个字符串是另一个字符串的子字符串时会出现问题,即:'HGNC:54' 和 'HGNC:549'
>>translation
'HGNC:54':0
'HGNC:549':1
结果:
>>gprs.head(1)
Rxn rule translation
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80 09 or 1 or 0
如何只替换整个字符串而不替换子字符串?
编辑编辑:
适用于:
for_eval = {k+'(?![0-9])' : str(v) for k, v in translation.items()}
gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
感谢 EFT 的建议
这是一种可用于计算酶是否表达的方法。
代码:
import re
RE_GENE_NAME = re.compile(r'(HGNC:[0-9]+)')
def calc_expressed(translation_table, rule_str):
rule_expr = RE_GENE_NAME.sub(r'translation_table[""]', rule_str)
return eval(rule_expr)
它是如何工作的?
这里的想法是采用如下规则:
HGNC:253 or HGNC:549
并将其更改为:
translation_table["HGNC:253"] or translation_table["HGNC:549"]
IE: 将 HGNC:1234
等值的所有实例更改为 translation_table["HGNC:1234"]
。
这会产生一个字符串,它是一个合法的 python 表达式。结果表达式可以用 eval()
.
求值
测试代码:
translation = {
'HGNC:80': 1,
'HGNC:249': 1,
'HGNC:250': 1,
'HGNC:251': 0,
'HGNC:252': 1,
'HGNC:253': 0,
'HGNC:255': 1,
'HGNC:256': 1,
'HGNC:549': 0,
'HGNC:550': 1,
'HGNC:2602': 0,
'HGNC:16354': 1,
}
test_rules = (
('HGNC:550', 1),
('HGNC:2602', 0),
('HGNC:253 or HGNC:549', 0),
('HGNC:549 or HGNC:550 or HGNC:80', 1),
('HGNC:549 or (HGNC:550 and HGNC:2602)', 0),
('HGNC:549 or (HGNC:550 and HGNC:16354)', 1),
('HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251)', 1)
)
for rule, expected in test_rules:
assert expected == calc_expressed(translation, rule)
输入翻译可以用
完成
>>>for_eval = {k+'(?![0-9])': str(v) for k, v in translation.items()}
>>>gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
解释:
第一行
>>>for_eval = {k+'(?![0-9])': str(v) for k, v in translation.items()}
将 0
和 1
分别替换为它们的字符串形式 '0'
和 '1'
,为将它们插入第二行的字符串做准备。将“(?![0-9])”添加到密钥会检查并忽略后面有更多数字的匹配项,从而避免仅匹配密钥的第一部分。
第二行
>>>gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
在 pandas 中将替换作为列操作执行,而不是在 python 中迭代每一行,对于较大的数据集,在这种情况下说 30 个或更多条目,速度要慢得多.
如果没有 regex=True
,这将只适用于完全匹配,会出现与您在尝试实施更长规则时遇到的相同问题。
示例,感谢 u/Stephen Rauch 的测试用例:
In [3]:translation = {
'HGNC:80': 1,
'HGNC:249': 1,
'HGNC:250': 1,
'HGNC:251': 0,
'HGNC:252': 1,
'HGNC:253': 0,
'HGNC:255': 1,
'HGNC:256': 1,
'HGNC:549': 0,
'HGNC:550': 1,
'HGNC:2602': 0,
'HGNC:16354': 1,
}
In [4]:gprs = pd.DataFrame([
('HGNC:550', 1),
('HGNC:2602', 0),
('HGNC:253 or HGNC:549', 0),
('HGNC:549 or HGNC:550 or HGNC:80', 1),
('HGNC:549 or (HGNC:550 and HGNC:2602)', 0),
('HGNC:549 or (HGNC:550 and HGNC:16354)', 1),
('HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251)', 1)
], columns = ['rule', 'target'])
In [5]:for_eval = {k: str(v) for k, v in translation.items()}
In [6]:gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
In [7]:gprs['translation']
Out[7]:
0 1
1 0
2 0 or 0
3 0 or 1 or 1
4 0 or (1 and 0)
5 0 or (1 and 1)
6 1 or (1 and 1) or (1 and 0)
Name: translation, dtype: object
对于您稍后要看的第二部分,eval
,如 u/Stephen Rauch 的回答中所述和详述,可用于评估生成的字符串中包含的表达式。为此,与使用 iterrows
相比,pd.Series.map
可用于对系列更快地应用逐元素操作。在这里,看起来像这样
In [10]:gprs['translation'].map(eval)
Out[10]:
0 1
1 0
2 0
3 1
4 0
5 1
6 1
Name: translation, dtype: int64
或者,如果试图勉强获得最后一点性能,可以选择在输出上使用正则表达式模式匹配而不是映射。它变得更加依赖于你的规则是如何表达的,但是如果它们的格式都和你的 post 中的三个一样好,"and"s 都是成对的并且用括号括起来,没有嵌套,那么
# set any 'and' term with a zero in it to zero
>>>ands = gprs['translation'].str.replace('0 and \d|\d and 0', '0')
# if any ones remain, only 'or's and '1 and 1' statements are left
>>>ors = ands.replace('1', 1, regex=True)
# faster to force it to numeric than to search the remaining terms for zeros
>>>out = pd.to_numeric(ors, errors='coerce').fillna(0)
>>>out
0 1.0
1 0.0
2 0.0
3 1.0
4 0.0
5 1.0
6 1.0
Name: translation, dtype: float64
应该快五倍左右,使用 timeit 模块检查,超过几千行,盈亏平衡点大约为 60 或 70 个条目。
我有一个 pandas.DataFrame
,其中包含说明酶是否表达的布尔规则。有些规则很简单(表达取决于 1 个基因),有些规则更复杂(表达取决于几个基因)
>>gprs.head()
Out[362]:
Rxn rule
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80
6 24_25VITD2Hm HGNC:2602
8 25VITD2Hm HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251) or (HGNC:250 and HGNC:251) or HGNC:252 or HGNC:253 or HGNC:255 or HGNC:256
...
一个包含基因表达信息的字典对象:(1=expr, 0=not expr)
>>translation
'HGNC:80':1
'HGNC:2602':0
etc...
我想将 'translation' 对象中包含的表达式信息替换到我的 'gprs' pandas.DataFrame
中。到目前为止我有:
for index, row in gprs.iterrows():
row['rule']=row['rule'].replace(r'(', "")
row['rule']=row['rule'].replace(r')', "")
ruleGenes=re.split(" and | or ",(row['rule']))
for gene in ruleGenes:
if re.match("HGNC:HGNC:", gene):
gene=gene[5:]
try:
gprs=gprs.replace(gene,translation[gene])
except:
print 'error in ', gene
else:
try:
gprs=gprs.replace(gene,translation[gene])
except:
print 'error in ', gene
这仅在规则很简单(1 个元素)但对更复杂的规则无效时有效:
>>gprs.head()
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80
6 24_25VITD2Hm 0
7 24_25VITD3Hm HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251) or (HGNC:250 and HGNC:251) or HGNC:252 or HGNC:253 or HGNC:255 or HGNC:256
最终我想用 max() 函数替换 'or',用 min() 函数替换 'and' 并评估布尔规则。
有什么建议吗?
编辑:
使用 EFT 代码时,当一个字符串是另一个字符串的子字符串时会出现问题,即:'HGNC:54' 和 'HGNC:549'
>>translation
'HGNC:54':0
'HGNC:549':1
结果:
>>gprs.head(1)
Rxn rule translation
0 13DAMPPOX HGNC:549 or HGNC:550 or HGNC:80 09 or 1 or 0
如何只替换整个字符串而不替换子字符串?
编辑编辑:
适用于:
for_eval = {k+'(?![0-9])' : str(v) for k, v in translation.items()}
gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
感谢 EFT 的建议
这是一种可用于计算酶是否表达的方法。
代码:
import re
RE_GENE_NAME = re.compile(r'(HGNC:[0-9]+)')
def calc_expressed(translation_table, rule_str):
rule_expr = RE_GENE_NAME.sub(r'translation_table[""]', rule_str)
return eval(rule_expr)
它是如何工作的?
这里的想法是采用如下规则:
HGNC:253 or HGNC:549
并将其更改为:
translation_table["HGNC:253"] or translation_table["HGNC:549"]
IE: 将 HGNC:1234
等值的所有实例更改为 translation_table["HGNC:1234"]
。
这会产生一个字符串,它是一个合法的 python 表达式。结果表达式可以用 eval()
.
测试代码:
translation = {
'HGNC:80': 1,
'HGNC:249': 1,
'HGNC:250': 1,
'HGNC:251': 0,
'HGNC:252': 1,
'HGNC:253': 0,
'HGNC:255': 1,
'HGNC:256': 1,
'HGNC:549': 0,
'HGNC:550': 1,
'HGNC:2602': 0,
'HGNC:16354': 1,
}
test_rules = (
('HGNC:550', 1),
('HGNC:2602', 0),
('HGNC:253 or HGNC:549', 0),
('HGNC:549 or HGNC:550 or HGNC:80', 1),
('HGNC:549 or (HGNC:550 and HGNC:2602)', 0),
('HGNC:549 or (HGNC:550 and HGNC:16354)', 1),
('HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251)', 1)
)
for rule, expected in test_rules:
assert expected == calc_expressed(translation, rule)
输入翻译可以用
完成>>>for_eval = {k+'(?![0-9])': str(v) for k, v in translation.items()}
>>>gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
解释:
第一行
>>>for_eval = {k+'(?![0-9])': str(v) for k, v in translation.items()}
将 0
和 1
分别替换为它们的字符串形式 '0'
和 '1'
,为将它们插入第二行的字符串做准备。将“(?![0-9])”添加到密钥会检查并忽略后面有更多数字的匹配项,从而避免仅匹配密钥的第一部分。
第二行
>>>gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
在 pandas 中将替换作为列操作执行,而不是在 python 中迭代每一行,对于较大的数据集,在这种情况下说 30 个或更多条目,速度要慢得多.
如果没有 regex=True
,这将只适用于完全匹配,会出现与您在尝试实施更长规则时遇到的相同问题。
示例,感谢 u/Stephen Rauch 的测试用例:
In [3]:translation = {
'HGNC:80': 1,
'HGNC:249': 1,
'HGNC:250': 1,
'HGNC:251': 0,
'HGNC:252': 1,
'HGNC:253': 0,
'HGNC:255': 1,
'HGNC:256': 1,
'HGNC:549': 0,
'HGNC:550': 1,
'HGNC:2602': 0,
'HGNC:16354': 1,
}
In [4]:gprs = pd.DataFrame([
('HGNC:550', 1),
('HGNC:2602', 0),
('HGNC:253 or HGNC:549', 0),
('HGNC:549 or HGNC:550 or HGNC:80', 1),
('HGNC:549 or (HGNC:550 and HGNC:2602)', 0),
('HGNC:549 or (HGNC:550 and HGNC:16354)', 1),
('HGNC:16354 or (HGNC:249 and HGNC:250) or (HGNC:249 and HGNC:251)', 1)
], columns = ['rule', 'target'])
In [5]:for_eval = {k: str(v) for k, v in translation.items()}
In [6]:gprs['translation'] = gprs['rule'].replace(for_eval, regex=True)
In [7]:gprs['translation']
Out[7]:
0 1
1 0
2 0 or 0
3 0 or 1 or 1
4 0 or (1 and 0)
5 0 or (1 and 1)
6 1 or (1 and 1) or (1 and 0)
Name: translation, dtype: object
对于您稍后要看的第二部分,eval
,如 u/Stephen Rauch 的回答中所述和详述,可用于评估生成的字符串中包含的表达式。为此,与使用 iterrows
相比,pd.Series.map
可用于对系列更快地应用逐元素操作。在这里,看起来像这样
In [10]:gprs['translation'].map(eval)
Out[10]:
0 1
1 0
2 0
3 1
4 0
5 1
6 1
Name: translation, dtype: int64
或者,如果试图勉强获得最后一点性能,可以选择在输出上使用正则表达式模式匹配而不是映射。它变得更加依赖于你的规则是如何表达的,但是如果它们的格式都和你的 post 中的三个一样好,"and"s 都是成对的并且用括号括起来,没有嵌套,那么
# set any 'and' term with a zero in it to zero
>>>ands = gprs['translation'].str.replace('0 and \d|\d and 0', '0')
# if any ones remain, only 'or's and '1 and 1' statements are left
>>>ors = ands.replace('1', 1, regex=True)
# faster to force it to numeric than to search the remaining terms for zeros
>>>out = pd.to_numeric(ors, errors='coerce').fillna(0)
>>>out
0 1.0
1 0.0
2 0.0
3 1.0
4 0.0
5 1.0
6 1.0
Name: translation, dtype: float64
应该快五倍左右,使用 timeit 模块检查,超过几千行,盈亏平衡点大约为 60 或 70 个条目。