当前位置: 首页 > 面试题库 >

新的Dataframe列作为其他行(pandas)的通用函数

翁昊乾
2023-03-14
问题内容

在中创建 DataFrame 其他列中 最快的列的最快(最有效)方法是pandas 什么?

考虑以下示例:

import pandas as pd

d = {
    'id': [1, 2, 3, 4, 5, 6],
    'word': ['cat', 'hat', 'hag', 'hog', 'dog', 'elephant']
}
pandas_df = pd.DataFrame(d)

产生:

   id word
0   1  cat
1   2  hat
2   3  hag
3   4  hog
4   5  dog
5   6  elephant

假设我想创建一个新列bar,该列包含一个值,该值基于使用函数foo将当前行中的单词与中的其他行进行比较的输出而得出dataframe。

def foo(word1, word2):
    # do some calculation
    return foobar  # in this example, the return type is numeric

threshold = some_threshold

for index, _id, word in pandas_df.itertuples():
    value = sum(
        pandas_df[pandas_df['word'] != word].apply(
            lambda x: foo(x['word'], word),
            axis=1
        ) < threshold
    )
    pandas_df.loc[index, 'bar'] = value

这的确产生了正确的输出,但是它使用了itertuples()and apply(),这对于large而言并不是很有效DataFrames。

有没有一种方法可以矢量化(正确的术语?)这种方法?还是有另一种更好(更快)的方法来做到这一点?

注释/更新:

在原始帖子中,我使用了“编辑距离/左手脚距”作为foo函数。我改变了这个问题,试图变得更通用。想法是要应用的功能是将当前行的值与所有其他行进行比较,并返回一些汇总值。
如果foowasnltk.metrics.distance.edit_distance和thethreshold被设置为2(如原始文章中所示),则会产生以下输出:

   id word        bar
0   1  cat        1.0
1   2  hat        2.0
2   3  hag        2.0
3   4  hog        2.0
4   5  dog        1.0
5   6  elephant   0.0

我也有同样的问题的spark dataframes为好。我认为将这些分成两个职位是很有意义的,因此它们的范围不太广。但是,我通常发现,pandas有时可以修改类似问题的解决方案以使其适用spark。

灵感来自这个答案我的spark版本这个问题,我试图用一个笛卡尔积在pandas。我的速度测试表明速度稍快(尽管我怀疑这可能随数据大小而变化)。不幸的是,我仍然无法拨通电话apply()。

示例代码:

from nltk.metrics.distance import edit_distance as edit_dist

pandas_df2 = pd.DataFrame(d)

i, j = np.where(np.ones((len(pandas_df2), len(pandas_df2))))
cart = pandas_df2.iloc[i].reset_index(drop=True).join(
    pandas_df2.iloc[j].reset_index(drop=True), rsuffix='_r'
)

cart['dist'] = cart.apply(lambda x: edit_dist(x['word'], x['word_r']), axis=1)
pandas_df2 = (
    cart[cart['dist'] < 2].groupby(['id', 'word']).count()['dist'] - 1
).reset_index()

问题答案:

让我们尝试分析一下问题:

如果有N行,则N*N在相似性函数中要考虑“对”。在一般情况下,对所有这些元素进行评估都是无可避免的(听起来很合理,但我无法证明这一点)。因此,您至少具有O(n ^ 2)个时间复杂度。

但是,您可以尝试使用时间复杂度恒定的因素。我发现的可能选项是:

1.并行化:
由于您有一些大型的DataFrame,并行处理是最佳的选择。这将使您(几乎)在时间复杂度方面得到线性改善,因此,如果您有16名工作人员,您将获得(几乎)16倍的改进。

例如,我们可以将的行划分df为不相交的部分,并分别处理每个部分,然后合并结果。一个非常基本的并行代码可能看起来像这样:

from multiprocessing import cpu_count,Pool

def work(part):
    """
    Args:
        part (DataFrame) : a part (collection of rows) of the whole DataFrame.

    Returns:
        DataFrame: the same part, with the desired property calculated and added as a new column
    """
     # Note that we are using the original df (pandas_df) as a global variable
     # But changes made in this function will not be global (a side effect of using multiprocessing).
    for index, _id, word in part.itertuples(): # iterate over the "part" tuples
        value = sum(
            pandas_df[pandas_df['word'] != word].apply( # Calculate the desired function using the whole original df
                lambda x: foo(x['word'], word),
                axis=1
            ) < threshold
        )
        part.loc[index, 'bar'] = value
    return part

# New code starts here ...

cores = cpu_count() #Number of CPU cores on your system

data_split = np.array_split(data, cores) # Split the DataFrame into parts
pool = Pool(cores) # Create a new thread pool
new_parts = pool.map(work , data_split) # apply the function `work` to each part, this will give you a list of the new parts
pool.close() # close the pool
pool.join()
new_df = pd.concat(new_parts) # Concatenate the new parts

注意:我试图使代码尽可能接近OP的代码。这只是一个基本的演示代码,并且存在许多更好的替代方法。

2.“低级”优化:
另一个解决方案是尝试优化相似度函数的计算和迭代/映射。与上一个或下一个选项相比,我认为这不会为您带来很大的提速。

3.取决于功能的修剪:
您可以尝试的最后一件事是依赖相似功能的改进。这在一般情况下不起作用,但如果您可以分析相似性函数,则将很好地工作。例如:

假设您使用的是Levenshtein距离(LD),则可以观察到任意两个字符串之间的距离> =长度之间的差。即LD(s1,s2) >= abs(len(s1)-len(s2))。

您可以使用此观察结果来修剪可能的相似对,以进行评估。因此,对于每个字符串长度l1,只有具有长度的字符串比较它l2有abs(l1-l2) <= limit。(限制为所接受的最大相似度,在您提供的示例中为2)。

另一个观察是LD(s1,s2) = LD(s2,s1)。这样可以将对数减少2倍。

该解决方案实际上可能使您陷入O(n)时间复杂性(高度依赖于数据)的问题。
为什么?你可能会问。
这是因为,如果我们有10^9行,但平均而言10^3,每行只有“接近”长度的行,那么我们需要针对约10^9 * 10^3 /2对而不是10^9 * 10^9对来评估函数。但这(再次)取决于数据。如果(在此示例中)您拥有长度均为3的字符串,则此方法将无用。



 类似资料:
  • 问题内容: 在中创建 其他列中 最快的列的最快(最有效)方法 是 什么? 考虑以下示例: 产生: 假设我想创建一个新列,该列包含一个值,该值基于使用函数将当前行中的单词与中的其他行进行比较的输出而得出。 这的确产生了正确的输出,但是它使用了and ,这对于large而言并不是很有效。 有没有一种方法可以 矢量化 (正确的术语?)这种方法?还是有另一种更好(更快)的方法来做到这一点? 在原始帖子中,

  • 我想将我的自定义函数(它使用if-else梯形)应用到数据帧每行中的这六列(,,,,,)。 我已经尝试了不同的方法从其他问题,但似乎仍然不能找到正确的答案,我的问题。关键的一点是,如果这个人被算作西班牙裔,他们就不能算作其他任何东西。即使他们在另一个种族栏中有一个“1”,他们仍然被算作西班牙裔,而不是两个或两个以上的种族。类似地,如果所有ERI列的总和大于1,则被计为两个或两个以上的种族,不能被计

  • 考虑下面的数据集存储在熊猫数据文件<代码> DFX < /代码>: 我有一个函数是: 现在,我想在中创建一个新列,其中包含计算出的z值 查看其他SO示例,我尝试了几个变体,包括: 返回错误。正确的方法是什么?

  • 在我的scala程序中,我有一个dataframe,其中有两列和(类型都为)。除此之外,我有一个先前定义的对象和一些方法和属性。在这里,我想要使用dataframe的当前值和中的属性向dataframe添加一个新列。 例如,如果我有下面的dataframe: 谢谢你。

  • 我有一个数据框,如: 我需要为每个列应用一些函数,并在这个数据帧中创建具有特殊名称的新列。 所以我需要根据列和(如name)乘以两个额外的列,名称为和由两个。是否可以使用或其他结构来完成此操作?

  • 问题内容: 我想申请我的自定义函数(它使用的梯)这六个列我的数据帧的每一行中)。 我尝试了与其他问题不同的方法,但似乎仍然找不到适合我问题的正确答案。关键在于,如果该人被视为西班牙裔,就不能被视为其他任何人。即使他们在另一个种族栏中的得分为“ 1”,他们仍然被视为西班牙裔,而不是两个或两个以上的种族。同样,如果所有ERI列的总和大于1,则将它们计为两个或多个种族,并且不能计为唯一的种族(西班牙裔除

  • 当我试图找到这个答案时,我可能使用了糟糕的搜索词。现在,在索引DataFrame之前,我以这种方式在列中获得一个值列表... …然后我将在列上设置索引。这似乎是浪费的一步。在索引上尝试上述操作时,我得到一个键错误。 如何获取索引中的值(单个和多个)并将其放入列表或元组列表中?

  • 问题内容: 如果我有这样的话: 给出以下结构: 如何将其扩展到以下内容? 问题答案: 您可以用来遍历每一行,并使用列表推导将数据重塑为所需的形式: 产量 ivakar的解决方案,是最快的: 这是用于上述基准测试的设置: