Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 基于列表理解的加速函数_Python_Pandas_List Comprehension - Fatal编程技术网

Python 基于列表理解的加速函数

Python 基于列表理解的加速函数,python,pandas,list-comprehension,Python,Pandas,List Comprehension,我试图为每个用户获取15个最相关的项目,但我尝试的每个功能都花了很长时间。(6个多小时后我关闭了它…) 我有418个独特的用户,3718个独特的项目。 U2tfifd dict还有418个条目,tfidf_功能_名称中有32645个单词。 我的互动形状(40733,3) 我试过: def index_tfidf_users(user_id) : return [users for users in U2tfifd[user_id].flatten().tolist()] def

我试图为每个用户获取15个最相关的项目,但我尝试的每个功能都花了很长时间。(6个多小时后我关闭了它…)

我有418个独特的用户,3718个独特的项目。 U2tfifd dict还有418个条目,tfidf_功能_名称中有32645个单词。 我的互动形状(40733,3)

我试过:

  def index_tfidf_users(user_id) : 
    return [users for users in U2tfifd[user_id].flatten().tolist()]

def get_relevant_items(user_id):
    return sorted(zip(tfidf_feature_names, index_tfidf_users(user_id)), key=lambda x: -x[1])[:15]

def get_tfidf_token(user_id) : 
    return [words for words, values in get_relevant_items(user_id)]
然后
interactions\u full\u df[“tags”]=interactions\u full\u df[“user\u id”]。应用(lambda x:get\u tfidf\u token(x))


U2tfifd是一个dict,其键=用户id,值=数组

有几种情况可能会导致代码性能低下。每种方法的效果都取决于Python版本(2.x或3.x)、RAM速度等等。您需要自己对各种潜在的改进进行试验和基准测试

1.TFIDF稀疏度(约10倍的加速比,取决于稀疏度) 一个明显的潜在问题是TFIDF自然返回稀疏数据(例如,一个段落不会像整本书那样使用任何地方的唯一单词),当几乎所有地方的数据都可能为零时,使用numpy数组等密集结构是一个奇怪的选择

如果您将来要进行同样的分析,那么制作/使用具有稀疏数组输出的TFIDF版本可能会有所帮助,以便在提取令牌时可以跳过零值。这可能会为每个用户提供整个稀疏阵列的第二个好处,即安装在缓存中并防止在排序和其他操作中进行代价高昂的RAM访问

不管怎样,还是值得对数据进行稀疏化。在我的土豆上,一个与你相似的数据快速基准表明这个过程可以在30秒内完成。这个过程用一个高度优化的例程替换了您正在做的大部分工作,该例程用C编写,并用Python打包使用。唯一的实际成本是通过非零项的第二次传递,但除非该传递从一开始就非常有效,否则最好使用稀疏数据

2.重复的工作和记忆(~100倍的加速) 如果
U2tfifd
有418个条目,并且
interactions\u full\u df
有40733行,那么您对
get\u tfidf\u token()。有很多备忘录装饰器,但是你不需要任何非常复杂的用例

def memoize(f):
    _cache = {}
    def _f(arg):
        if arg not in _cache:
            _cache[arg] = f(arg)
        return _cache[arg]
    return _f

@memoize
def get_tfidf_token(user_id):
    ...
如果将其分解,函数
memoize()
将返回另一个函数。该函数的行为是在计算预期返回值并在必要时存储它之前,检查本地缓存中的预期返回值

语法
@memoize…
是以下内容的缩写

def uncached_get_tfidf_token(user_id):
    ...
get_tfidf_token = memoize(uncached_get_tfidf_token)
@
符号用于表示我们希望修改或装饰
get_tfidf_token()
的版本,而不是原始版本。根据应用程序的不同,将装饰器链接在一起可能会有所帮助

3.矢量化操作(不同的加速比,必要时进行基准测试) Python不像其他语言那样真正有基本类型的概念,甚至整数在我的机器上的内存中也占用
24
字节。列表通常不会被打包,因此当您费力地浏览它们时,可能会导致代价高昂的缓存未命中。无论CPU在排序等方面所做的工作有多少,重击一个全新的内存块以将数组变成一个列表,并且只使用一次全新的、昂贵的内存都会导致性能下降

您尝试做的许多事情都有fast(SIMD矢量化、并行化、内存效率高、压缩内存和其他有趣的优化)numpy等价物,并避免不必要的数组拷贝和类型转换。看起来您已经在使用numpy了,所以您不会有任何额外的导入或依赖项

例如,
zip()。为了计算这些索引,您可以使用如下方法,这避免了不必要的列表创建,并使用了一个渐进复杂度稍好的优化例程作为额外的奖励

def get_tfidf_token(user_id):
    temp = U2tfifd[user_id].flatten()
    ind = np.argpartition(temp, len(temp)-15)[-15:]
    return tfidf_feature_names[ind]  # works if tfidf_feature_names is a numpy array
    return [tfidf_feature_names[i] for i in ind]  # always works
根据
U2tfifd[user\u id]
的形状,您可以通过将
参数传递给
np.argsort()
并将获得的15个索引展平来避免代价高昂的
展平()
计算

4.奖金
sorted()
函数支持一个
reverse
参数,这样可以避免额外的计算,比如在每个值上都抛出一个负数。简单使用

sorted(..., reverse=True)
更好的是,因为您实际上并不关心排序本身,而只关心您可以忽略的15个最大值

sorted(...)[-15:]
索引最大的15,而不是反转排序并取最小的15。如果您在应用程序中使用更好的函数,如
np.argpartition()
,这实际上并不重要,但它在将来可能会有所帮助

您还可以通过将
.apply(lambda x:get\u tfidf\u token(x))
替换为
.apply(get\u tfidf\u token)
来避免某些函数调用,因为
get\u tfidf\u token
已经是具有预期行为的函数。您实际上不需要额外的
lambda


就我所知,大多数额外的收益都是相当挑剔和依赖于系统的。例如,您可以使用Cython或straight C在足够的时间内使大多数事情变得更快,但是您已经有了相当快的例程,可以在开箱即用的情况下完成您想要的事情。额外的工程工作可能不值得任何潜在收益。

对于性能问题,相关信息包括每个对象的大小以及什么构成“永恒”。您有多少用户(甚至粗略估计),
sorted(..., reverse=True)
sorted(...)[-15:]