Как установить все значения существующего Pandas DataFrame равным нулю?

В настоящее время у меня есть существующий Pandas DataFrame с индексом даты и столбцами с определенным именем.

Что касается ячеек данных, они заполняются различными значениями float.

Я хотел бы скопировать мой DataFrame, но заменить все эти значения на нуль.

Цель состоит в том, чтобы повторно использовать структуру DataFrame (размеры, индекс, имена столбцов), но очистить все текущие значения, заменив их нулями.

В настоящее время я достигаю этого:

df[df > 0] = 0

Однако это не заменит никакого отрицательного значения в DataFrame.

Нет ли более общего подхода к заполнению всего существующего DataFrame одним общим значением?

Заранее благодарим вас за помощь.

Ответ 1

Самый быстрый способ, который также сохраняет dtypes, заключается в следующем:

for col in df.columns:
    df[col].values[:] = 0

Это напрямую записывает в базовый массив numy каждого столбца. Я сомневаюсь, что любой другой метод будет быстрее, чем этот, поскольку он не выделяет дополнительной памяти и не проходит через обработку панд dtype. Вы также можете использовать np.issubdtype только для обнуления числовых столбцов. Это, вероятно, то, что вам нужно, если у вас смешанный dtype DataFrame, но, конечно, в этом нет необходимости, если ваш DataFrame уже полностью числовой.

for col in df.columns:
    if np.issubdtype(df[col].dtype, np.number):
        df[col].values[:] = 0

Для небольших DataFrames проверка подтипа несколько затратна. Однако стоимость обнуления нечислового столбца является существенной, поэтому, если вы не уверены, является ли ваш DataFrame полностью числовым, вам следует включить проверку issubdtype.


Сроки сравнения

Настройка

import pandas as pd
import numpy as np

def make_df(n, only_numeric):
    series = [
        pd.Series(range(n), name="int", dtype=int),
        pd.Series(range(n), name="float", dtype=float),
    ]
    if only_numeric:
        series.extend(
            [
                pd.Series(range(n, 2 * n), name="int2", dtype=int),
                pd.Series(range(n, 2 * n), name="float2", dtype=float),
            ]
        )
    else:
        series.extend(
            [
                pd.date_range(start="1970-1-1", freq="T", periods=n, name="dt")
                .to_series()
                .reset_index(drop=True),
                pd.Series(
                    [chr((i % 26) + 65) for i in range(n)],
                    name="string",
                    dtype="object",
                ),
            ]
        )

    return pd.concat(series, axis=1)

>>> make_df(5, True)
   int  float  int2  float2
0    0    0.0     5     5.0
1    1    1.0     6     6.0
2    2    2.0     7     7.0
3    3    3.0     8     8.0
4    4    4.0     9     9.0

>>> make_df(5, False)
   int  float                  dt string
0    0    0.0 1970-01-01 00:00:00      A
1    1    1.0 1970-01-01 00:01:00      B
2    2    2.0 1970-01-01 00:02:00      C
3    3    3.0 1970-01-01 00:03:00      D
4    4    4.0 1970-01-01 00:04:00      E

Маленький DataFrame

n = 10_000                                                                                  

# Numeric df, no issubdtype check
%%timeit df = make_df(n, False)
for col in df.columns:
    df[col].values[:] = 0
36.1 µs ± 510 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

# Numeric df, yes issubdtype check
%%timeit df = make_df(n, False)
for col in df.columns:
    if np.issubdtype(df[col].dtype, np.number):
        df[col].values[:] = 0
53 µs ± 645 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

# Non-numeric df, no issubdtype check
%%timeit df = make_df(n, True)
for col in df.columns:
    df[col].values[:] = 0
113 µs ± 391 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

# Non-numeric df, yes issubdtype check
%%timeit df = make_df(n, True)
for col in df.columns:
    if np.issubdtype(df[col].dtype, np.number):
        df[col].values[:] = 0
39.4 µs ± 1.91 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Большой фрейм данных

n = 10_000_000                                                                             

# Numeric df, no issubdtype check
%%timeit df = make_df(n, False)
for col in df.columns:
    df[col].values[:] = 0
38.7 ms ± 151 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Numeric df, yes issubdtype check
%%timeit df = make_df(n, False)
for col in df.columns:
    if np.issubdtype(df[col].dtype, np.number):
        df[col].values[:] = 0
39.1 ms ± 556 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Non-numeric df, no issubdtype check
%%timeit df = make_df(n, True)
for col in df.columns:
    df[col].values[:] = 0
99.5 ms ± 748 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Non-numeric df, yes issubdtype check
%%timeit df = make_df(n, True)
for col in df.columns:
    if np.issubdtype(df[col].dtype, np.number):
        df[col].values[:] = 0
17.8 ms ± 228 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Ранее я предлагал ответ ниже, но теперь я считаю его вредным - он значительно медленнее, чем приведенные выше, и его сложнее рассуждать. Его единственное преимущество - писать приятнее.

Самый чистый способ - использовать голое двоеточие для ссылки на весь dataframe.

df[:] = 0

К сожалению, ситуация dtype немного размыта, потому что каждый столбец в результирующем кадре данных будет иметь тот же dtype. Если каждый столбец df изначально был float, новый dtypes все равно будет float. Но если один столбец был int или object, кажется, что новый dtypes будет все int.

Ответ 2

Вы можете использовать функцию replace :

df2 = df.replace(df, 0)

Ответ 3

Поскольку вы пытаетесь создать копию, может быть лучше просто создать новый кадр данных со значениями как 0, а столбцы и индекс из исходного фрейма данных:

pd.DataFrame(0, columns=df.columns, index=df.index)