Ответ 1
От взгляда на различные методы основная причина заключается в том, что построение массива numpy df.values
занимает львиную долю времени.
len(df)
и df.shape
Эти два являются быстрыми, потому что они по существу
len(df.index._data)
и
(len(df.index._data), len(df.columns._data))
где _data
- numpy.ndarray
. Таким образом, использование df.shape
должно быть в два раза быстрее, чем len(df)
, поскольку оно находит длину как df.index
, так и df.columns
(оба типа pd.Index
)
len(df.values)
и df.values.shape
Скажем, вы уже извлекли vals = df.values
. Тогда
In [1]: df = pd.DataFrame(np.random.rand(1000, 10), columns=range(10))
In [2]: vals = df.values
In [3]: %timeit len(vals)
10000000 loops, best of 3: 35.4 ns per loop
In [4]: %timeit vals.shape
10000000 loops, best of 3: 51.7 ns per loop
По сравнению с:
In [5]: %timeit len(df.values)
100000 loops, best of 3: 3.55 µs per loop
Таким образом, узкое место не len
, а построение df.values
. Если вы исследуете pandas.DataFrame.values()
, вы найдете (примерно эквивалентные) методы:
def values(self):
return self.as_matrix()
def as_matrix(self, columns=None):
self._consolidate_inplace()
if self._AXIS_REVERSED:
return self._data.as_matrix(columns).T
if len(self._data.blocks) == 0:
return np.empty(self._data.shape, dtype=float)
if columns is not None:
mgr = self._data.reindex_axis(columns, axis=0)
else:
mgr = self._data
if self._data._is_single_block or not self._data.is_mixed_type:
return mgr.blocks[0].get_values()
else:
dtype = _interleaved_dtype(self.blocks)
result = np.empty(self.shape, dtype=dtype)
if result.shape[0] == 0:
return result
itemmask = np.zeros(self.shape[0])
for blk in self.blocks:
rl = blk.mgr_locs
result[rl.indexer] = blk.get_values(dtype)
itemmask[rl.indexer] = 1
# vvv here is your final array assuming you actually have data
return result
def _consolidate_inplace(self):
def f():
if self._data.is_consolidated():
return self._data
bm = self._data.__class__(self._data.blocks, self._data.axes)
bm._is_consolidated = False
bm._consolidate_inplace()
return bm
self._protect_consolidate(f)
def _protect_consolidate(self, f):
blocks_before = len(self._data.blocks)
result = f()
if len(self._data.blocks) != blocks_before:
if i is not None:
self._item_cache.pop(i, None)
else:
self._item_cache.clear()
return result
Обратите внимание, что df._data
является pandas.core.internals.BlockManager
, а не a numpy.ndarray
.