Эффективно обновлять значения в фрейме данных Pandas со смешанными столбцами dtype - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть большой пандан DataFrame с формой (700 000, 5000), содержащий столбцы смешанных dtypes (в основном int8, некоторые float64 и пару datetime64 [ns]). Для каждой строки в кадре данных я хочу установить значение некоторых столбцов равным нулю, если другой столбец также равен нулю.

Если я переберу данные и установлю значения с помощью iloc, это будет очень медленно. Я пробовал и iterrows и itertuples, например.

1. iterrows

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

Я также пытался использовать индексацию панд, но она также очень медленная (хотя лучше, чем iterrows или itertuples).

3. Pandas Loc & iloc

df.loc[df.iloc[:, ix_1]==0, df.columns[ix_to_change]] = 0

Затем я попытался перейти к базовому массиву numpy, который прекрасно работает с точки зрения производительности, но у меня возникли проблемы с dtypes.

Он быстро перебирает базовый массив, но новый фрейм данных имеет все dty-типы 'object'. Если я попытаюсь установить dtypes для каждого столбца (как в этом примере), он потерпит неудачу в столбцах 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)

Есть ли лучший способ для меня сделать обновление в первую очередь?

Или, если нет, как мне сохранить мои dtypes одинаковыми (столбцы даты и времени не должны изменяться в списке столбцов в случае необходимости)

Или, может быть, есть лучший способ обновить исходный фрейм данных с помощью моего обновленного массива numpy, где я обновляю только измененные столбцы (все из которых являются int8)?

Обновление

Как и было запрошено в комментариях, вот минимальный пример, иллюстрирующий, как dtypes int8 становятся dtypes объекта после перехода в numpy. Чтобы было ясно, это проблема только для метода 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 голосов
/ 05 сентября 2018

Используется эффективность итерации 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 d *.

Я перебираю эти строки X (здесь гораздо эффективнее, чем в пандах), затем X.T дает мне массивы, которые я могу использовать для замены целых столбцов в пандах.

Это позволяет избежать медленных вызовов iloc / loc на большом фрейме данных, и я получаю неизменные dtypes во всех столбцах.

0 голосов
/ 05 сентября 2018

TL; DR

Для эффективности Pandas / NumPy не используйте смешанные типы (object dtype) в столбце . Существуют методы, позволяющие преобразовывать серии в числовые и эффективно управлять ими.


Вы можете использовать pd.DataFrame.select_dtypes для определения числовых столбцов. Предполагая, что это единственные, где вы хотите обновить значения, вы можете затем передать их в pd.DataFrame.loc.

Он быстро перебирает базовый массив, но новый у dataframe есть все dtypes 'object'.

Учитывая, что у вас осталось 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...