Python 创建新数据帧的性能

Python 创建新数据帧的性能,python,performance,pandas,numpy,Python,Performance,Pandas,Numpy,我对在以下环境中创建数据帧的时间安排感到非常惊讶: 原因是什么 为什么vs[]之间存在如此巨大的差异 @EdChum的评论是正确的 只要看看pandas是如何处理列表数据还是数组数据,您就会很快明白传递列表更为复杂 数组: elif isinstance(data, (np.ndarray, Series, Index)): if data.dtype.names: data_columns = list(data.dtype.names)

我对在以下环境中创建数据帧的时间安排感到非常惊讶:

原因是什么


为什么vs
[]
之间存在如此巨大的差异

@EdChum的评论是正确的

只要看看pandas是如何处理列表数据还是数组数据,您就会很快明白传递列表更为复杂

数组:

elif isinstance(data, (np.ndarray, Series, Index)):
            if data.dtype.names:
                data_columns = list(data.dtype.names)
                data = dict((k, data[k]) for k in data_columns)
                if columns is None:
                    columns = data_columns
                mgr = self._init_dict(data, index, columns, dtype=dtype)
            elif getattr(data, 'name', None):
                mgr = self._init_dict({data.name: data}, index, columns,
                                      dtype=dtype)
            else:
                mgr = self._init_ndarray(data, index, columns, dtype=dtype,copy=copy)
现在,如果它是一个列表:

elif isinstance(data, (list, types.GeneratorType)):
    if isinstance(data, types.GeneratorType):
        data = list(data)
    if len(data) > 0:
        if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1:
            if is_named_tuple(data[0]) and columns is None:
                columns = data[0]._fields
            arrays, columns = _to_arrays(data, columns, dtype=dtype)
            columns = _ensure_index(columns)

            # set the index
            if index is None:
                if isinstance(data[0], Series):
                    index = _get_names_from_index(data)
                elif isinstance(data[0], Categorical):
                    index = _default_index(len(data[0]))
                else:
                    index = _default_index(len(data))

            mgr = _arrays_to_mgr(arrays, columns, index, columns,
                                 dtype=dtype)
        else:
            mgr = self._init_ndarray(data, index, columns, dtype=dtype,
                                     copy=copy)

经过一些痛苦的调试后,我可以确认慢一点的程序,如下所示:

在这里,它测试传递数据的类型,因为它的列表类似于它然后尝试测试每个元素的类型,它不希望列表包含np数组,所以它出现在这里:

def _to_arrays(data, columns, coerce_float=False, dtype=None):
    """
    Return list of arrays, columns
    """
    if isinstance(data, DataFrame):
        if columns is not None:
            arrays = [data._ixs(i, axis=1).values
                      for i, col in enumerate(data.columns) if col in columns]
        else:
            columns = data.columns
            arrays = [data._ixs(i, axis=1).values for i in range(len(columns))]

        return arrays, columns

    if not len(data):
        if isinstance(data, np.ndarray):
            columns = data.dtype.names
            if columns is not None:
                return [[]] * len(columns), columns
        return [], []  # columns if columns is not None else []
    if isinstance(data[0], (list, tuple)):
        return _list_to_arrays(data, columns, coerce_float=coerce_float,
                               dtype=dtype)
那么这里:

def _list_to_arrays(data, columns, coerce_float=False, dtype=None):
    if len(data) > 0 and isinstance(data[0], tuple):
        content = list(lib.to_object_array_tuples(data).T)
    else:
        # list of lists
        content = list(lib.to_object_array(data).T)
    return _convert_object_array(content, columns, dtype=dtype,
                                 coerce_float=coerce_float)
最后,这里:

def _convert_object_array(content, columns, coerce_float=False, dtype=None):
    if columns is None:
        columns = _default_index(len(content))
    else:
        if len(columns) != len(content):  # pragma: no cover
            # caller's responsibility to check for this...
            raise AssertionError('%d columns passed, passed data had %s '
                                 'columns' % (len(columns), len(content)))

    # provide soft conversion of object dtypes
    def convert(arr):
        if dtype != object and dtype != np.object:
            arr = lib.maybe_convert_objects(arr, try_float=coerce_float)
            arr = _possibly_cast_to_datetime(arr, dtype)
        return arr

    arrays = [convert(arr) for arr in content]

    return arrays, columns
您可以看到,它执行的构造没有优化,它基本上只是迭代每个元素,转换它(它将复制它)并返回数组列表


另一条路,,由于np数组形状和数据类型对熊猫更友好,它可以查看数据或在需要时进行复制,但它已经知道了足够的信息来优化结构

我认为这里的主要区别在于,当传递
值时。重塑
形状和数据类型已经与熊猫兼容,并且它可以在没有任何分配的底层内存,而对于列表类型,它必须检测形状,然后推断兼容的数据类型,并将值复制到新分配的数据类型memory@EdChum-谢谢,这可能是答案…嗯,但我以两种方式传递numpy数组-或者我缺少什么?您通过执行
[df.numFruits.values]传递列表中的numpy数组
,您可以检查它
类型([df.numFruits.values])
将返回
列表
。因此,当它检查数据时,它将进入if的
isinstance(data,(list,types.generatorType))
部分。感谢您的回答,现在我对它有了更好的理解。我会等几个小时,然后接受最好的答案,现在是你的问题了。看看代码路径,它可以测试第一个元素是否是np数组,如果是的话,优化结构,使它可以走一条快速的路径,但它并不期望这一点,实际上,它增加了测试集元素或元组元素是否为ndarray的复杂性,这就是为什么它不进行测试的原因
def _list_to_arrays(data, columns, coerce_float=False, dtype=None):
    if len(data) > 0 and isinstance(data[0], tuple):
        content = list(lib.to_object_array_tuples(data).T)
    else:
        # list of lists
        content = list(lib.to_object_array(data).T)
    return _convert_object_array(content, columns, dtype=dtype,
                                 coerce_float=coerce_float)
def _convert_object_array(content, columns, coerce_float=False, dtype=None):
    if columns is None:
        columns = _default_index(len(content))
    else:
        if len(columns) != len(content):  # pragma: no cover
            # caller's responsibility to check for this...
            raise AssertionError('%d columns passed, passed data had %s '
                                 'columns' % (len(columns), len(content)))

    # provide soft conversion of object dtypes
    def convert(arr):
        if dtype != object and dtype != np.object:
            arr = lib.maybe_convert_objects(arr, try_float=coerce_float)
            arr = _possibly_cast_to_datetime(arr, dtype)
        return arr

    arrays = [convert(arr) for arr in content]

    return arrays, columns