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