当前位置: 首页 > 知识库问答 >
问题:

在DataFrame中反转列顺序的最大复杂性是什么?

宗政楚
2023-03-14

假设我在熊猫中有一个数据帧,有m行和n列。让我们也假设我想颠倒列的顺序,这可以通过以下代码来完成:

df_reversed = df[df.columns[::-1]]

这个操作的最大复杂性是什么?我假设这取决于列的数量,但也取决于行的数量吗?

共有3个答案

范云
2023-03-14

我在这里使用big_Ofitting库进行了一次实证测试

注:所有试验均在6个数量级的自变量上进行(即。

  • 1010^6常量的大小3

结果显示反向操作。列[::-1]数据帧中的复杂性为

  1. 立方体:O(n^3)其中n是行数
  2. 立方体:O(n^3)其中n是列数

先决条件:您需要使用终端命令安装big\u o()pip安装big\u o

密码

import big_o
import pandas as pd
import numpy as np

SWEAP_LOG10 = 6
COLUMNS = 3
ROWS = 10

def build_df(rows, columns):
    # To isolated the creation of the DataFrame from the inversion operation.
    narray = np.zeros(rows*columns).reshape(rows, columns)
    df = pd.DataFrame(narray)
    return df

def flip_columns(df):
    return df[df.columns[::-1]]

def get_row_df(n, m=COLUMNS):
    return build_df(1*10**n, m)

def get_column_df(n, m=ROWS):
    return build_df(m, 1*10**n)


# infer the big_o on columns[::-1] operation vs. rows
best, others = big_o.big_o(flip_columns, get_row_df, min_n=1, max_n=SWEAP_LOG10,n_measures=SWEAP_LOG10, n_repeats=10)

# print results
print('Measuring .columns[::-1] complexity against rapid increase in # rows')
print('-'*80 + '\nBig O() fits: {}\n'.format(best) + '-'*80)

for class_, residual in others.items():
    print('{:<60s}  (res: {:.2G})'.format(str(class_), residual))

print('-'*80)

# infer the big_o on columns[::-1] operation vs. columns
best, others = big_o.big_o(flip_columns, get_column_df, min_n=1, max_n=SWEAP_LOG10,n_measures=SWEAP_LOG10, n_repeats=10)

# print results
print()
print('Measuring .columns[::-1] complexity against rapid increase in # columns')
print('-'*80 + '\nBig O() fits: {}\n'.format(best) + '-'*80)

for class_, residual in others.items():
    print('{:<60s}  (res: {:.2G})'.format(str(class_), residual))
    
print('-'*80)

后果

Measuring .columns[::-1] complexity against rapid increase in # rows
--------------------------------------------------------------------------------
Big O() fits: Cubic: time = -0.017 + 0.00067*n^3
--------------------------------------------------------------------------------
Constant: time = 0.032                                        (res: 0.021)
Linear: time = -0.051 + 0.024*n                               (res: 0.011)
Quadratic: time = -0.026 + 0.0038*n^2                         (res: 0.0077)
Cubic: time = -0.017 + 0.00067*n^3                            (res: 0.0052)
Polynomial: time = -6.3 * x^1.5                               (res: 6)
Logarithmic: time = -0.026 + 0.053*log(n)                     (res: 0.015)
Linearithmic: time = -0.024 + 0.012*n*log(n)                  (res: 0.0094)
Exponential: time = -7 * 0.66^n                               (res: 3.6)
--------------------------------------------------------------------------------


Measuring .columns[::-1] complexity against rapid increase in # columns
--------------------------------------------------------------------------------
Big O() fits: Cubic: time = -0.28 + 0.009*n^3
--------------------------------------------------------------------------------
Constant: time = 0.38                                         (res: 3.9)
Linear: time = -0.73 + 0.32*n                                 (res: 2.1)
Quadratic: time = -0.4 + 0.052*n^2                            (res: 1.5)
Cubic: time = -0.28 + 0.009*n^3                               (res: 1.1)
Polynomial: time = -6 * x^2.2                                 (res: 16)
Logarithmic: time = -0.39 + 0.71*log(n)                       (res: 2.8)
Linearithmic: time = -0.38 + 0.16*n*log(n)                    (res: 1.8)
Exponential: time = -7 * 1^n                                  (res: 9.7)
--------------------------------------------------------------------------------
龙默
2023-03-14

大O复杂度(Pandas 0.24)是m*n,其中m是列数,n是行数。请注意,这是在使用DataFrame.__getitem__方法(又名[])和Index(请参阅相关代码,其他类型会触发副本)时使用的。

下面是一个有用的堆栈跟踪:

 <ipython-input-4-3162cae03863>(2)<module>()
      1 columns = df.columns[::-1]
----> 2 df_reversed = df[columns]

  pandas/core/frame.py(2682)__getitem__()
   2681             # either boolean or fancy integer index
-> 2682             return self._getitem_array(key)
   2683         elif isinstance(key, DataFrame):

  pandas/core/frame.py(2727)_getitem_array()
   2726             indexer = self.loc._convert_to_indexer(key, axis=1)
-> 2727             return self._take(indexer, axis=1)
   2728 

  pandas/core/generic.py(2789)_take()
   2788                                    axis=self._get_block_manager_axis(axis),
-> 2789                                    verify=True)
   2790         result = self._constructor(new_data).__finalize__(self)

  pandas/core/internals.py(4539)take()
   4538         return self.reindex_indexer(new_axis=new_labels, indexer=indexer,
-> 4539                                     axis=axis, allow_dups=True)
   4540 

  pandas/core/internals.py(4421)reindex_indexer()
   4420             new_blocks = self._slice_take_blocks_ax0(indexer,
-> 4421                                                      fill_tuple=(fill_value,))
   4422         else:

  pandas/core/internals.py(1254)take_nd()
   1253             new_values = algos.take_nd(values, indexer, axis=axis,
-> 1254                                        allow_fill=False)
   1255         else:

> pandas/core/algorithms.py(1658)take_nd()
   1657     import ipdb; ipdb.set_trace()
-> 1658     func = _get_take_nd_function(arr.ndim, arr.dtype, out.dtype, axis=axis,
   1659                                  mask_info=mask_info)
   1660     func(arr, indexer, out, fill_value)

L1660中的func调用熊猫/core/算法最终调用复杂度为O(m*n)的cython函数。这是原始数据的数据被复制到out的地方。out包含原始数据的反序副本。

    inner_take_2d_axis0_template = """\
    cdef:
        Py_ssize_t i, j, k, n, idx
        %(c_type_out)s fv

    n = len(indexer)
    k = values.shape[1]

    fv = fill_value

    IF %(can_copy)s:
        cdef:
            %(c_type_out)s *v
            %(c_type_out)s *o

        #GH3130
        if (values.strides[1] == out.strides[1] and
            values.strides[1] == sizeof(%(c_type_out)s) and
            sizeof(%(c_type_out)s) * n >= 256):

            for i from 0 <= i < n:
                idx = indexer[i]
                if idx == -1:
                    for j from 0 <= j < k:
                        out[i, j] = fv
                else:
                    v = &values[idx, 0]
                    o = &out[i, 0]
                    memmove(o, v, <size_t>(sizeof(%(c_type_out)s) * k))
            return

    for i from 0 <= i < n:
        idx = indexer[i]
        if idx == -1:
            for j from 0 <= j < k:
                out[i, j] = fv
        else:
            for j from 0 <= j < k:
                out[i, j] = %(preval)svalues[idx, j]%(postval)s
"""

注意,在上面的模板函数中,有一个使用memmove的路径(这是本例中采用的路径,因为我们正在从int64映射到int64,并且输出的维度与我们切换索引时的维度相同)。请注意,memmove仍然是O(n),与它必须复制的字节数成比例,尽管可能比直接写入索引要快。

白念
2023-03-14

我不知道熊猫是如何实现这一点的,但我确实进行了经验测试。我运行了以下代码(在Jupyter笔记本中)来测试操作的速度:

def get_dummy_df(n):
    return pd.DataFrame({'a': [1,2]*n, 'b': [4,5]*n, 'c': [7,8]*n})

df = get_dummy_df(100)
print df.shape
%timeit df_r = df[df.columns[::-1]]

df = get_dummy_df(1000)
print df.shape
%timeit df_r = df[df.columns[::-1]]

df = get_dummy_df(10000)
print df.shape
%timeit df_r = df[df.columns[::-1]]

df = get_dummy_df(100000)
print df.shape
%timeit df_r = df[df.columns[::-1]]

df = get_dummy_df(1000000)
print df.shape
%timeit df_r = df[df.columns[::-1]]

df = get_dummy_df(10000000)
print df.shape
%timeit df_r = df[df.columns[::-1]]

产出为:

(200, 3)
1000 loops, best of 3: 419 µs per loop
(2000, 3)
1000 loops, best of 3: 425 µs per loop
(20000, 3)
1000 loops, best of 3: 498 µs per loop
(200000, 3)
100 loops, best of 3: 2.66 ms per loop
(2000000, 3)
10 loops, best of 3: 25.2 ms per loop
(20000000, 3)
1 loop, best of 3: 207 ms per loop

如您所见,在前3种情况下,操作的开销是花费大部分时间(400-500µs),但从第4种情况开始,所花费的时间开始与数据量成正比,以数量级增加每一次。

所以,假设n也必须有一个比例,似乎我们正在处理O(m*n)

 类似资料:
  • 问题内容: 我想在主要评论之后显示最新评论及其回复。显然,答复将具有最新日期,因此我无法按日期对日期进行排序,因为答复将位于主要日期之上。我不确定如何通过一个查询正确执行此操作。任何想法都可以。 数据库转储:http : //pastie.org/576963 问题答案: 我猜文章的“回复ID”为0,评论的文章号为。如果这是您的设计,这应该可以工作: 添加: 感谢您在评论中提供其他信息。将结果按所

  • 我使用最大堆来查找数组中的k个最大元素,如下所示: 1) 我已经构建了给定数组的前k个元素(arr[0]到arr[k-1])的最小堆MH。O(k) 2)对于每个元素,在第k个元素(arr[k]到arr[n-1])之后,将其与MH的根进行比较。 ……a)如果元素大于根,则将其设为根,并为MH调用heapify ……b)否则忽略它。 //步骤2是O((n-k)) 3)最后,MH有k个最大元素,MH的根

  • 问题内容: 在使用Python Pandas进行读写时,是否可以保留csv文件中列的顺序?例如,在此代码中 输出文件可能会有所不同,因为未保留列。 问题答案: 当前版本的Pandas(‘0.11.0’)中似乎存在一个错误,这意味着Matti John的答案将不起作用。如果您指定要写入文件的列,则它们将按字母顺序书写,而只是根据cols中的列表重新标记。例如,此代码: 导致以下(错误)输出: 您可以

  • 问题内容: 我有一个简单的场景: 基本上如果 我想要,如果 我已经尝试了许多在网络上找到的变体,例如,什么都没有。.似乎无法使其正常工作 有任何想法吗? 问题答案: 如果您想更改顺序并使用更大的尺寸,可以使用bootstrap提供。看起来,如果只想更改尺寸,则必须在一个较大尺寸的小提琴上定义常规顺序

  • 本文向大家介绍C#实现复杂XML的序列化与反序列化,包括了C#实现复杂XML的序列化与反序列化的使用技巧和注意事项,需要的朋友参考一下 本文以一个实例的形式讲述了C#实现复杂XML的序列化与反序列化的方法。分享给大家供大家参考。具体方法如下: 已知.xml(再此命名default.xml)文件,请将其反序列化到一个实例对象。 Default.XML文件如下: C#示例代码如下: PS:这里再为大家

  • 问题内容: 如何找到特定列的值 最大的行 ? 将为我提供每一列的最大值,我不知道如何获取对应的行。 问题答案: 使用熊猫功能。很简单: 或者,您也可以使用,例如-它提供相同的功能,并且至少与粗略观察中的显示速度一样快。 返回索引标签,而不是整数。 示例”:如果您将字符串值作为索引标签,例如行“ a”至“ e”,则可能想知道最大值出现在第4行(而不是“ d”行)。 如果您希望该标签在其中的整数位置,