В идеале при копировании кадра данных pandas мы должны использовать .copy()
, который по умолчанию является глубокой копией.
Мы также могли бы добиться того же, используя new_df = pd.Dataframe(old_df)
, который также интуитивно понятен (и является общим стилем для большинства языков программирования, поскольку в принципе мы называем конструктор копирования).
В обоих случаях они имеют разные идентификаторы памяти, как показано ниже. Но когда я меняю old_df
с помощью .iloc
, это меняет значение new_df
. Это ожидаемое поведение? Я не мог понять это поведение, используя документы. Я упускаю что-то тривиальное?
# ! pip install smartprint
from smartprint import smartprint as sprint
import pandas as pd
data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
original_df = pd.DataFrame(data)
new_df = pd.DataFrame(original_df)
original_df['A'] = original_df['A'].apply(lambda x: x * 1000)
print ("============ Changing a column with pd.Dataframe(old_df); No change")
sprint(original_df)
sprint(new_df)
sprint (id(new_df), id(original_df))
data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
original_df = pd.DataFrame(data)
new_df = pd.DataFrame(original_df)
original_df.iloc[0,:] = 20
print ("============ Using .iloc with pd.Dataframe(old_df); Change")
sprint(original_df)
sprint(new_df)
sprint (id(new_df), id(original_df))
data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
original_df = pd.DataFrame(data)
new_df = original_df.copy()
original_df.iloc[0,:] = 20
print ("============ Using .iloc with old_df.copy(); No change")
sprint(original_df)
sprint(new_df)
sprint (id(new_df), id(original_df))
Выход:
============ Changing a column with pd.Dataframe(old_df); No change
original_df : A B
0 1000 4
1 2000 5
2 3000 6
new_df : A B
0 1 4
1 2 5
2 3 6
id(new_df), id(original_df) : 140529132556544 140529514510944
============ Using .iloc with pd.Dataframe(old_df); Change
original_df : A B
0 20 20
1 2 5
2 3 6
new_df : A B
0 20 20
1 2 5
2 3 6
id(new_df), id(original_df) : 140528893052000 140528894065584
============ Using .iloc with old_df.copy(); No change
original_df : A B
0 20 20
1 2 5
2 3 6
new_df : A B
0 1 4
1 2 5
2 3 6
id(new_df), id(original_df) : 140529057828336 140528940223984
Мои версии Python и Pandas перечислены ниже:
import sys
sys.version
Out[16]: '3.8.17 (default, Jul 5 2023, 16:18:40) \n[Clang 14.0.6 ]'
pd.__version__
Out[17]: '1.4.3'
Это ожидаемое поведение DataFrame, это также происходит, когда вы передаете в качестве входных данных массив numpy:
a = np.array([[1,2],[3,4]])
df = pd.DataFrame(a)
a[0][0] = 9
print(df)
0 1
0 99 2
1 3 4
На самом деле это хорошо описано в документации DataFrame:
копировать: bool или None, по умолчанию None
Скопируйте данные из входов. Для данных dict значение по умолчанию None ведет себя как copy=True. Для ввода DataFrame или 2d ndarray по умолчанию установлено значение «Нет». ведет себя как copy=False. Если данные представляют собой словарь, содержащий один или несколько Серии (возможно, разных типов), copy=False гарантирует, что эти входные данные не копируются.
a = np.array([[1,2],[3,4]])
df = pd.DataFrame(a, copy=True)
a[0][0] = 99
print(df)
0 1
0 1 2
1 3 4
В этом случае вы не изменяете столбец, а перезаписываете его новым. Это означает, что базовые объекты теперь другие:
a = np.array([[1,2],[3,4]])
df = pd.DataFrame(a)
df[0] = 99
print(a)
[[1 2]
[3 4]]
Теперь это имеет смысл, как вы это объясняете. Тем не менее, я нахожу это нелогичным, но это просто мысли бывшего Java-разработчика :), отмечающего как принятое.