如何从我的数据框中有效地为列的子集获取numpy数组?

时间:2017-05-30 23:39:13

标签: python pandas numpy

动机

我经常回答一些问题,其中我主张将数据帧值转换为基础numpy数组,以便更快地进行计算。但是,有一些注意事项可以做到这一点,并且有些方法比其他方法更好。

我将提供自己的答案,以回馈社区。我希望你们发现它有用。

问题
考虑数据框df

df = pd.DataFrame(dict(A=[1, 2, 3], B=list('xyz'), C=[9, 8, 7], D=[4, 5, 6]))
print(df)

   A  B  C  D
0  1  x  9  4
1  2  y  8  5
2  3  z  7  6

dtypes

print(df.dtypes)

A     int64
B    object
C     int64
D     int64
dtype: object

我想创建一个由a列和A列组成的numpy数组C。假设可能有很多列,并且我的目标是两个特定列AC

我尝试了什么

我能做到:

df[['A', 'C']].values

array([[1, 9],
       [2, 8],
       [3, 7]])

这是准确的!

但是,我可以用numpy

更快地完成
p = [df.columns.get_loc(i) for i in ['A', 'C']]
df.values[:, p]

array([[1, 9],
       [2, 8],
       [3, 7]], dtype=object)

这更快,但不准确。请注意dtype=object。我需要整数!。

p = [df.columns.get_loc(i) for i in ['A', 'C']]
df.values[:, p].astype(int)

array([[1, 9],
       [2, 8],
       [3, 7]])

现在这是正确的,但我可能不知道我有所有整数。

计时

# Clear and accurate, but slower
%%timeit 
df[['A', 'C']].values
1000 loops, best of 3: 347 µs per loop

# Not accurate, but close and fast
%%timeit 
p = [df.columns.get_loc(i) for i in ['A', 'C']]
df.values[:, p]
10000 loops, best of 3: 59.2 µs per loop

# Accurate for this test case and fast, needs to be more generalized.
%%timeit 
p = [df.columns.get_loc(i) for i in ['A', 'C']]
df.values[:, p].astype(int)
10000 loops, best of 3: 59.3 µs per loop

2 个答案:

答案 0 :(得分:4)

pandas values属性中的整个数据框存储单个数组。当您在数据帧上调用values属性时,它会从存储的基础对象(即pd.Series对象)构建数组。将数据框视为pd.Series的{​​{1}}非常有用,其中每列都是数据框所包含的pd.Series。每列可以有一个与其余列不同的pd.Series。这是数据帧如此有用的部分原因。但是,numpy数组必须有一种类型。当我们在数据框上调用dtype属性时,它会转到每个列并从每个相应的values属性中提取数据并将它们拼凑在一起。如果列各自的dtypes不一致,则生成的数组的values将被强制为dtype

选项1
缓慢但准确

object

这很慢的原因是因为你要求pandas为你构建一个新的数据帧a = df[['A', 'C']].values 然后通过点击每个新数据帧的列的values属性来构建数组df[['A', 'C']]。 p>

选项2
查找列位置,然后切片a

values

这样做更好,因为我们只构建values数组而不重建新的数据帧。我相信我们正在获得一个具有一致dtypes的数组。如果需要进行上升,我不会在这里处理它。

选项3
我的首选方法
仅访问我关注的列的值

c = ['A', 'C']
p = [df.columns.get_loc(i) for i in c]
a = df.values[:, p].astype(df.dtypes[c[0]])

这会将pandas数据框架作为a = np.column_stack([df[col].values for col in ['A', 'C']]) 的容器,在其中我只访问我关心的列的pd.Series属性。然后我从这些数组中构建一个新数组。如果需要解决铸造问题,numpy会处理它。

所有方法都会产生相同的结果

values

<强>时序
小数据

array([[1, 9],
       [2, 8],
       [3, 7]])

大数据

%%timeit 
a = df[['A', 'C']].values
1000 loops, best of 3: 338 µs per loop

%%timeit 
c = ['A', 'C']
p = [df.columns.get_loc(i) for i in c]
a = df.values[:, p].astype(df.dtypes[c[0]])
10000 loops, best of 3: 166 µs per loop

%timeit np.column_stack([df[col].values for col in ['A', 'C']])
The slowest run took 7.36 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8.97 µs per loop

答案 1 :(得分:1)

试试这个:

np.array(zip(df['A'].values, df['C'].values))

timeit:

%%timeit
np.array(zip(df['A'].values, df['C'].values))

最慢的运行时间比最快的运行时长5.51倍。这可能意味着正在缓存中间结果。 10000个循环,最佳3:每循环17.8μs