使用混合的dtype列有效更新熊猫数据框中的值

时间:2018-09-05 07:19:11

标签: python pandas numpy

我有一个形状为(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

2 个答案:

答案 0 :(得分:1)

TL; DR

为了提高熊猫/ 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。