使用三元条件运算符对系列进行 Python 字符串连接
Posted
技术标签:
【中文标题】使用三元条件运算符对系列进行 Python 字符串连接【英文标题】:Python string concatenation on series with ternary condition operator 【发布时间】:2017-12-04 01:30:24 【问题描述】:我想添加一个列,该列应该表示基于其他列连接的 Pandas 数据框的 URL。另外我想添加一个条件。
目前是这样的
matches['url'] = 'http://www.example.org' +
matches['column1'] +
'/' +
(matches['id'].str[-3:] if matches['id'].str.contains('M|-0') else matches['id'].str[-4:]) +
'/xyz.pdf'
我有问题的条件是这个:(matches['id'].str[-3:] if matches['id'].str.contains('M|-0') else matches['id'].str[-4:])
这应该执行以下操作:如果matches['id']
包含字符串M
或-0
,那么matches['id'].str[-3:]
应该发生(即,取matches['id']
列的最后3 个字符,否则取matches['id'].str[-4:]
应该发生。
但是,我收到以下错误:
ValueError:Series 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。
我知道我可以使用apply()
创建一个中间列并在其中编码条件。但我想用一个很好的单线来做,我认为我离解决方案不太远。感谢您的帮助。
【问题讨论】:
我猜你应该在创建字符串之前将字符串的部分构造为变量。在创建字符串时,使用.format()
。
你不能那样做,因为三元运算符没有向量化。它同时对整个系列执行 if-then,而不是对每个单独的元素。您必须使用 .map
或 .apply
单独构建字符串的各个部分。
【参考方案1】:
我认为你需要 numpy.where
与 Series
完美搭配的东西:
mask = matches['id'].str.contains('M|-0')
matches['url'] = 'http://www.example.org' + matches['column1'] + '/' +
np.where(mask, matches['id'].str[-3:], matches['id'].str[-4:]) + '/xyz.pdf'
示例:
matches = pd.DataFrame('id':['2010-M012','2010-1234','2010-1234'],
'column1':['s','d','m'])
print (matches)
column1 id
0 s 2010-M012
1 d 2010-1234
2 m 2010-1234
mask = matches['id'].str.contains('M|-0')
matches['url'] = 'http://www.example.org' + matches['column1'] + '/' + \
np.where(mask, matches['id'].str[-3:], matches['id'].str[-4:]) + '/xyz.pdf'
matches['url1'] = 'http://www.example.org' + matches['column1'] + '/' + \
matches['id'].map(lambda x : x[-3:] if (('M' in x) or ('-0' in x)) else x[-4:]) + '/xyz.pdf'
matches['url2'] = matches.apply(lambda x: 'http://www.example.org//xyz.pdf'.format(x['column1'], x['id'][-3:] if (('M' in x['id']) or ('-0' in x['id'])) else x['id'][-4:]), axis=1)
print (matches)
column1 id url \
0 s 2010-M012 http://www.example.orgs/012/xyz.pdf
1 d 2010-1234 http://www.example.orgd/1234/xyz.pdf
2 m 2010-1234 http://www.example.orgm/1234/xyz.pdf
url1 url2
0 http://www.example.orgs/012/xyz.pdf http://www.example.orgs/012/xyz.pdf
1 http://www.example.orgd/1234/xyz.pdf http://www.example.orgd/1234/xyz.pdf
2 http://www.example.orgm/1234/xyz.pdf http://www.example.orgm/1234/xyz.pdf
时间安排:
matches = pd.DataFrame('id':['2010-M012','2010-1234','2010-1234'],
'column1':['s','d','m'])
#[30000 rows x 2 columns]
matches = pd.concat([matches]*10000).reset_index(drop=True)
In [168]: %timeit matches['url'] = 'http://www.example.org' + matches['column1'] + '/' + np.where(matches['id'].str.contains('M|-0'), matches['id'].str[-3:], matches['id'].str[-4:]) + '/xyz.pdf'
10 loops, best of 3: 50.9 ms per loop
In [169]: %timeit matches['url1'] = 'http://www.example.org' + matches['column1'] + '/' + matches['id'].map(lambda x : x[-3:] if (('M' in x) or ('-0' in x)) else x[-4:]) + '/xyz.pdf'
10 loops, best of 3: 22.1 ms per loop
In [170]: %timeit matches['url2'] = matches.apply(lambda x: 'http://www.example.org//xyz.pdf'.format(x['column1'], x['id'][-3:] if (('M' in x['id']) or ('-0' in x['id'])) else x['id'][-4:]), axis=1)
1 loop, best of 3: 1.07 s per loop
【讨论】:
map()
方法效果很好。非常感谢您。有趣的是np.where()
不起作用。我做的和你做的一模一样。我没有遇到异常,但字符串匹配 (str.contains()
) 似乎不起作用。你知道为什么会这样吗?
没有数据的难题。掩码返回正确的True
和False
值?
可能需要mask = matches['id'].str.contains('^M|^-0')
来匹配字符串的开头。
实际上我的字符串看起来像这样2010-M012
(匹配)或2010-1234
(不匹配)。但我刚刚发现map()
对我也不起作用.. 它总是匹配字符串,即使M
或-0
没有出现。
@beta - 地图解决方案获胜,检查时间。【参考方案2】:
变化:
(matches['id'].str[-3:] if matches['id'].str.contains('M|-0') else matches['id'].str[-4:])
到:
np.where(matches['id'].str.contains('M|-0'), matches['id'].str[-3:],matches['id'].str[-4:])
看看它是否有效。
【讨论】:
以上是关于使用三元条件运算符对系列进行 Python 字符串连接的主要内容,如果未能解决你的问题,请参考以下文章