Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.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 在DataFrame中反转列顺序的最大复杂性是什么?_Python_Algorithm_Pandas_Numpy_Big O - Fatal编程技术网

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

Python 在DataFrame中反转列顺序的最大复杂性是什么?,python,algorithm,pandas,numpy,big-o,Python,Algorithm,Pandas,Numpy,Big O,假设我有一个熊猫数据框,有m行和n列。我们还可以说,我想反转列的顺序,这可以通过以下代码完成: df_reversed = df[df.columns[::-1]] 这个操作的最大复杂性是什么?我假设这取决于列的数量,但也取决于行的数量吗?我不知道Pandas是如何实现这一点的,但我确实进行了经验测试。我运行了以下代码(在Jupyter笔记本中)来测试操作的速度: def get_dummy_df(n): return pd.DataFrame({'a': [1,2]*n, 'b':

假设我有一个熊猫数据框,有m行和n列。我们还可以说,我想反转列的顺序,这可以通过以下代码完成:

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

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

我不知道Pandas是如何实现这一点的,但我确实进行了经验测试。我运行了以下代码(在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)

我使用
big\u O
拟合库进行了一次实证测试

:所有测试均在6个数量级(

  • 10
    10^6
    与常量
    的大小3
  • 10
    10^6
    与常量
    的大小
结果表明,
数据帧
中的
反向操作
。列[:-1]
复杂度为

  • 立方体
    O(n^3)
    其中n是
    行数
  • 立方体
    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)
    
    大O复杂度(从0.24开始)是
    m*n
    ,其中
    m
    是列数,
    n
    是行数。注意,这是在使用
    数据帧时使用的。uu getitem_uuu
    方法(aka
    []
    )和
    索引(

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

     <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)
    

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

    self注意:在设置此类测试时,选择最低数量级:)会使笔记本电脑崩溃。如果要提高性能,请使用切片
    df.iloc[:,:-1]
    ,它返回一个视图,因此实际上应该是自由的,而不是
    df[df.columns[:-1]]
    在后者中建立索引时创建副本。@Divakar,作为一般规则,这是否仅适用于
    iloc
    ,还是
    loc
    也返回视图?可能超出了单个注释的范围,但我也感兴趣的是,为什么通过
    df[col_list]
    直接索引应该返回一个副本(这是设计选择/副作用/有任何好处吗?)@divakar如果我返回一个视图,我还可以对它进行操作,然后再次颠倒列的顺序,并以应用操作的原始数据帧结束吗?@TimHoldsworth一旦进行操作,就创建一个副本。你忽略了小数量级的开销,是的,从技术上讲,如果是O(n),O(n^3)也适用,“但是它不是很有用。”物理学家,你能详细说明一下小数量级的开销吗?另一个答案做得很好。你只需拟合有限数量的点,低尾巴就把你甩了。另一个答案实际上是看大O。我严重怀疑pandas中的任何基本操作都是O(n^3)。在调用
    \uuu getitem\uuuuu
    和调用cython函数后,我继续添加了一些示例,这些示例显示了更多的上下文,而cython函数最终大部分时间都花在了大值上。
    \uuuu getitem\uuuu
    中的逻辑并不总是直观的,但我发现这个GH问题有助于解释不同输入在引擎盖下的作用。谢谢,这对解释我们看到的行为有很大帮助。
     <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)
    
        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
    """