python - 从 .apply() 更改为使用列表理解将一个数据帧与一列列表与另一个数据帧中的值进行比较的函数
问题描述
简单地说,我想把下面的代码改成一个不使用apply
or的函数progress_apply
,这样在 2000 万+行上执行性能不需要 4+ 小时。
d2['B'] = d2['C'].progress_apply(lambda x: [z for y in d1['B'] for z in y if x.startswith(z)])
d2['B'] = d2['B'].progress_apply(max)
完整问题如下:
我有两个数据框。第一个数据框有一列有 4 个类别(A、B、C、D),其中有四个不同的数字列表,我想与第二个数据框中的列进行比较,这不是第一个数据框中的列表,而是以第一个数据帧中的一个或多个值开头的单个值。因此,在执行一些列表推导以在第二个数据帧的新列中返回匹配值列表后,最后一步是获取每个列表每行的这些值的最大值:
d1 = pd.DataFrame({'A' : ['A', 'B', 'C', 'D'],
'B' : [['84'], ['8420', '8421', '8422', '8423', '8424', '8425', '8426'], ['847', '8475'], ['8470', '8471']]})
A B
0 A [84]
1 B [8420, 8421, 8422, 8423, 8424, 8425, 8426]
2 C [847, 8475]
3 D [8470, 8471]
d2 = pd.DataFrame({'C' : [8420513, 8421513, 8426513, 8427513, 8470513, 8470000, 8475000]})
C
0 8420513
1 8421513
2 8426513
3 8427513
4 8470513
5 8470000
6 8475000
我目前的代码是这样的:
from tqdm import tqdm, tqdm_notebook
tqdm_notebook().pandas()
d1 = pd.DataFrame({'A' : ['A', 'B', 'C', 'D'], 'B' : [['84'], ['8420', '8421', '8422', '8423', '8424', '8425', '8426'], ['847', '8475'], ['8470', '8471']]})
d2 = pd.DataFrame({'C' : [8420513, 8421513, 8426513, 8427513, 8470513, 8470000, 8475000]})
d2['C'] = d2['C'].astype(str)
d2['B'] = d2['C'].progress_apply(lambda x: [z for y in d1['B'] for z in y if x.startswith(z)])
d2['B'] = d2['B'].progress_apply(max)
d2
并成功返回此输出:
C B
0 8420513 8420
1 8421513 8421
2 8426513 8426
3 8427513 84
4 8470513 8470
5 8470000 8470
6 8475000 8475
问题在于tqdm
进度条估计代码将需要4-5 小时才能在我的实际 DataFrame 上运行 2000 万行以上。我知道.apply
应该避免这种情况,并且自定义函数可以更快,这样我就不必逐行进行。我通常可以更改apply
为一个函数,但我正在为这个特定的函数而苦苦挣扎。我想我很遥远,但我会分享我尝试过的东西:
def func1(df, d2C, d1B):
return df[[z for y in d1B for z in y if z in d2C]]
d2['B'] = func1(d2, d2['C'], d1['B'])
d2
使用此代码,我收到ValueError: Wrong number of items passed 0, placement implies 1
并且仍然需要包含代码以获取每行每个列表的最大值。
解决方案
让我们尝试使用explode
和正则表达式extract
:
d1e = d1['B'].explode()
regstr = '('+'|'.join(sorted(d1e)[::-1])+')'
d2['B'] = d2['C'].astype('str').str.extract(regstr)
输出:
C B
0 8420513 8420
1 8421513 8421
2 8426513 8426
3 8427513 84
4 8470513 8470
5 8470000 8470
6 8475000 8475
因为,.str 访问比列表理解慢
import re
regstr = '|'.join(sorted(d1e)[::-1])
d2['B'] = [re.match(regstr, i).group() for i in d2['C'].astype('str')]
时间:
from timeit import timeit
import re
d1 = pd.DataFrame({'A' : ['A', 'B', 'C', 'D'], 'B' : [['84'], ['8420', '8421', '8422', '8423', '8424', '8425', '8426'], ['847', '8475'], ['8470', '8471']]})
d2 = pd.DataFrame({'C' : [8420513, 8421513, 8426513, 8427513, 8470513, 8470000, 8475000]})
d2['C'] = d2['C'].astype(str)
def orig(d):
d['B'] = d['C'].apply(lambda x: [z for y in d1['B'] for z in y if x.startswith(z)])
d['B'] = d['B'].apply(max)
return d
def comtorecords(d):
d['B']=[max([z for y in d1.B for z in y if str(row[1]) .startswith(z)]) for row in d.to_records()]
return d
def regxstracc(d):
d1e = d1['B'].explode()
regstr = '('+'|'.join(sorted(d1e)[::-1])+')'
d['B'] = d['C'].astype('str').str.extract(regstr)
return d
def regxcompre(d):
regstr = '|'.join(sorted(d1e)[::-1])
d['B'] = [re.match(regstr, i).group() for i in d['C'].astype('str')]
return d
res = pd.DataFrame(
index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
columns='orig comtorecords regxstracc regxcompre'.split(),
dtype=float
)
for i in res.index:
d = pd.concat([d2]*i)
for j in res.columns:
stmt = '{}(d)'.format(j)
setp = 'from __main__ import d, {}'.format(j)
print(stmt, d.shape)
res.at[i, j] = timeit(stmt, setp, number=100)
# res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);
res.plot(loglog=True);
输出:
推荐阅读
- html - Flask 不显示图标
- json - 更新 postgresql 中的典型 json
- google-bigquery - 使用 Cloud SQL 的 Bigquery 定价
- python - python xarray在多线程中很慢
- spring-boot - ElasticSearch RestHighLevelClient 在连接重置时重新连接
- python-3.x - 如何使用 unittest 在嵌套构造函数中模拟属性
- c++ - 包含中的递归(文件 1 包含包含文件 1 的文件 2)
- express - useQuery 钩子(apolloClient)和 getStaticProps(NextJS)的结果之间的区别
- server-side-rendering - ERROR 错误:未捕获(承诺中):TypeError:i.BehaviorSubject 不是 Angular 10 SSR 中的构造函数
- java - 是否有可能将 HTML 文件呈现为 Java Swing 组件(JPanel 等)?