pandas 使用 series.values 获取 numpy ndarray

pandas get numpy ndarray using series.values

我想将 series 转换为 numpy.ndarray,这样使用 ndarray 可以大大提高时间效率,

numpy_martix = df[some_col].values

我发现 series.values 本身需要一些时间来进行转换,所以我想知道是否有更快的方法来进行转换。

(已编辑)

当您调用 arr = df.values 时,会返回对 df 数据的引用,因此速度非常快(没有完成真正的工作)。另一方面,arr = df[list_of_cols].values 需要先在 df 内部进行一些整合。

试试运行这样:

arr = df.values[:, numeric_list_of_cols]

它可能会快一点,因为所有的工作都是在 numpy 数组中完成的。但实际的加速很可能取决于底层数据。

测试

我决定 运行 一些测试,结果如下。

首先,一个只包含数值的数据框。

'''Setup'''
a = np.random.rand(1000, 1000)
df = pd.DataFrame(a)
idx = np.arange(0, 1000, 3)

df.iloc[:3,:5]
Out[35]: 
          0         1         2         3         4
0  0.825100  0.556511  0.445429  0.972720  0.726258
1  0.818005  0.298689  0.684203  0.722038  0.848657
2  0.426488  0.270172  0.400533  0.946921  0.745236

让我们获取每三列:

# data frame:
%timeit x = df.iloc[:,idx]
1000 loops, best of 3: 1.69 ms per loop
%timeit x = df.iloc[:,idx].copy()
100 loops, best of 3: 2.75 ms per loop

# underlying values:
%timeit x = df.values[:,idx]
1000 loops, best of 3: 1.61 ms per loop
%timeit x = df.values[:,idx].copy()
100 loops, best of 3: 2.23 ms per loop

# numpy array for comparison
%timeit x = a[:,idx]
1000 loops, best of 3: 1.53 ms per loop
%timeit x = a[:,idx].copy()
100 loops, best of 3: 2.16 ms per loop

访问 .values 只快一点点(事实上,在其他测试中我 运行 差异甚至更小,不到 1%)。但是,让我们对一组连续的列进行同样的尝试。

%timeit x = df.iloc[:,300:600]
10000 loops, best of 3: 153 µs per loop
%timeit x = df.iloc[:,300:600].copy()
1000 loops, best of 3: 1.18 ms per loop

%timeit x = df.values[:,300:600]
The slowest run took 9.67 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 15.7 µs per loop
%timeit x = df.values[:,300:600].copy()
1000 loops, best of 3: 568 µs per loop

%timeit x = a[:,300:600]
The slowest run took 24.73 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 414 ns per loop
%timeit x = a[:,300:600].copy()
1000 loops, best of 3: 497 µs per loop

我们可以怀疑我们有时会看到。因此,让我们专注于 .copy() 的结果。使用 values 访问大约快 2 倍。

我们可以做得更好。让我们将底层数组中的数据布局更改为 fort运行 顺序。这意味着数组的列在内存中连续放置(而不是行,这是默认设置)。

a = np.asfortranarray(a)
df = pd.DataFrame(np.asfortranarray(a))
df.iloc[:3,:5]
df.iloc[:3,:5]
Out[53]: 
          0         1         2         3         4
0  0.825100  0.556511  0.445429  0.972720  0.726258
1  0.818005  0.298689  0.684203  0.722038  0.848657
2  0.426488  0.270172  0.400533  0.946921  0.745236

我只粘贴复制的结果:

# Every third column:
%timeit x = df.iloc[:,idx].copy()
100 loops, best of 3: 1.85 ms per loop
%timeit x = df.values[:,idx].copy()
1000 loops, best of 3: 1.2 ms per loop
%timeit x = a[:,idx].copy()
1000 loops, best of 3: 1.13 ms per loop

# Contiguous group of columns
%timeit x = df.iloc[:,300:600].copy()
1000 loops, best of 3: 635 µs per loop
%timeit x = df.values[:,300:600].copy()
1000 loops, best of 3: 655 µs per loop
%timeit x = a[:,300:600].copy()
1000 loops, best of 3: 586 µs per loop

但是当数据框包含混合类型的列时会发生什么? 让我们将每隔一列转换为字符串。

for i in range(0, 1000, 2):
    df[i] = df[i].astype(str)

df.iloc[:3,:5]
Out[71]: 
                0         1               2         3               4
0  0.825100137204  0.556511  0.445428873093  0.972720  0.726258247769
1  0.818005069404  0.298689  0.684203047084  0.722038  0.848656512757
2   0.42648763586  0.270172  0.400532581854  0.946921  0.745235906595

%timeit x = df.iloc[:,idx].copy()
100 loops, best of 3: 8.24 ms per loop
%timeit x = df.values[:,idx].copy()
10 loops, best of 3: 51.6 ms per loop

%timeit x = df.iloc[:,300:600].copy()
100 loops, best of 3: 6.91 ms per loop
%timeit x = df.values[:,300:600].copy()
10 loops, best of 3: 48.3 ms per loop

Numpy 不能很好地处理数组中的混合类型。直接访问数据框胜出。


附录 如何从 list_of_columns.

中得到 numeric_list_of_cols
  1. 纯python:

    cols = df.columns.tolist()
    numeric_list_of_cols = [cols.index(i) for i in list_of_columns]
    
  2. Numpy:

    numeric_lis_of_cols, = np.in1d(df.columns, list_of_columns).nonzero()
    

    numeric_lis_of_cols 后的逗号是解压元组所必需的。函数 in1d returns 布尔数组和 nonzero() - 非零索引数组的元组。

    警告:它可以改变元素的顺序。

    为了保持顺序,您可以迭代 list_of_columns 的元素(类似于 np.nonzero(df.columns == elem))以获得后续索引。