我有一个形状为(700,000,5,000)的大熊猫DataFrame,其中包含混合dtypes列(主要是int8,一些float64和几个datetime64 [ns])。对于数据框中的每一行,如果另一列也等于零,我想将某些列的值设置为零。
如果我遍历数据框并使用iloc设置值,那将非常慢。我已经尝试过迭代和迭代,例如。
1。
ix_1 = 3
ix_to_change = [20, 24, 51] # Actually it is almost 5000 columns to change
for i, row in df.iterrows():
if not row[ix_1]:
df.iloc[i, ix_to_change] = 0
2。 itertuples:
ix_1 = 3
ix_to_change = [20, 24, 51] # Actually it is almost 5000 columns to change
for row in df.itertuples():
if not row[ix_1 + 1]:
df.iloc[row[0], ix_to_change] = 0
我也尝试过使用熊猫索引,但是它也非常慢(尽管比迭代或迭代更好)。
3。 pandas loc&iloc
df.loc[df.iloc[:, ix_1]==0, df.columns[ix_to_change]] = 0
然后我尝试下拉至底层numpy数组,该数组在性能方面可以正常工作,但是我遇到了dtypes问题。
它快速遍历基础数组,但是新的数据框具有所有“对象” dtypes。如果我尝试按列设置dtype(如本例所示),则它在datetime列上将失败-可能是因为它们包含NaT项。
4。 numpy
X = df.values
for i, x in enumerate(X):
if not x[ix_1]:
X[i].put(ix_to_change, 0)
original_dtypes = df.dtypes
df = pd.DataFrame(data=X, index=df.index, columns=df.columns)
for col, col_dtype in original_dtypes.items():
df[c] = df[c].astype(col_dtype)
我是否有更好的方法来进行更新?
或者,如果不是,我应该如何保持我的dtype相同(如果相关,datetime列不在要更改的列列表中)?
或者也许有更好的方法让我使用更新的numpy数组更新原始数据帧,而我仅更新更改的列(所有列都为int8)?
按照注释中的要求,这是一个最小的示例,说明将int8 dtypes放入numpy后如何成为对象dtypes。需要明确的是,这只是上面方法4的问题(这是我到目前为止唯一的非慢速方法-如果可以解决此dtype问题):
import pandas as pd
df = pd.DataFrame({'int8_col':[10,11,12], 'float64_col':[1.5, 2.5, 3.5]})
df['int8_col'] = df['int8_col'].astype('int8')
df['datetime64_col'] = pd.to_datetime(['2018-01-01', '2018-01-02', '2018-01-03'])
>>> df.dtypes
float64_col float64
int8_col int8
datetime64_col datetime64[ns]
dtype: object
X = df.values
# At this point in real life I modify the int8 column(s) only in X
new_df = pd.DataFrame(data=X, index=df.index, columns=df.columns)
>>> new_df.dtypes
float64_col object
int8_col object
datetime64_col object
dtype: object
答案 0 :(得分:1)
为了提高熊猫/ NumPy的效率,请不要在列内使用混合类型(object
dtype) 。有一些方法可以将序列转换为数值,然后有效地对其进行操作。
您可以使用pd.DataFrame.select_dtypes
确定数字列。假设这些是唯一要更新值的值,则可以将其提供给pd.DataFrame.loc
。
它快速遍历底层数组,但是新的 数据框具有所有“对象” dtypes。
考虑到您还有object
dtype系列,看来您对ix_to_change
的定义包括非数字系列。在这种情况下,应将所有数字列转换为数字dtype 。例如,使用pd.to_numeric
:
df[ix_to_change] = df[ix_to_change].apply(pd.to_numeric, errors='coerce')
如果您要这样做,Pandas / NumPy将不会在性能方面帮助object
dtype系列。这些系列在内部以一系列指针表示,就像list
。
下面是一个示例,演示您可以做什么:
import pandas as pd, numpy as np
df = pd.DataFrame({'key': [0, 2, 0, 4, 0],
'A': [0.5, 1.5, 2.5, 3.5, 4.5],
'B': [2134, 5634, 134, 63, 1234],
'C': ['fsaf', 'sdafas',' dsaf', 'sdgf', 'fdsg'],
'D': [np.nan, pd.to_datetime('today'), np.nan, np.nan, np.nan],
'E': [True, False, True, True, False]})
numeric_cols = df.select_dtypes(include=[np.number]).columns
df.loc[df['key'] == 0, numeric_cols] = 0
结果:
A B C D E key
0 0.0 0 fsaf NaT True 0
1 1.5 5634 sdafas 2018-09-05 False 2
2 0.0 0 dsaf NaT True 0
3 3.5 63 sdgf NaT True 4
4 0.0 0 fdsg NaT False 0
按预期,没有转换为数字列的object
dtype系列:
print(df.dtypes)
A float64
B int64
C object
D datetime64[ns]
E bool
key int64
dtype: object
答案 1 :(得分:0)
这在更新值时利用了NumPy迭代的效率,并且还解决了dtype问题。
# numpy array of rows. Only includes columns to update (all int8) so dtype doesn't change
X = df.iloc[:, ix_to_change].values
# Set index on key to allow enumeration to match index
key_col = df.iloc[:, ix_1]
key_col.index = range(len(key_col))
# Set entire row (~5000 values) to zeros. More efficient than updating element-wise.
zero_row = np.zeros(X.shape[1])
for i, row in enumerate(X):
if key_col[i] == 0:
X[i] = zero_row
# Transpose to get array of column arrays.
# Each column array creates and replaces a Series in the DataFrame
for i, row in enumerate(X.T):
df[df.columns[ix_to_change[i]]] = row
X是一个NumPy数组,其中只有我要“归零”的列,它们都是int8 dtype。
我遍历了这X行(在这里比在熊猫中要高效得多),然后X.T为我提供了可用于替换熊猫中整列的数组。
这避免了在大数据帧上进行缓慢的iloc / loc调用,并且我最终在所有列上使用了不变的dtype。