Trong quá trình trả lời câu hỏi này cho bản thân, tôi đã học được nhiều điều, và tôi muốn tập hợp một danh mục các ví dụ và một số giải thích.
Câu trả lời cụ thể cho quan điểm của cuộc levelstranh luận sẽ đến ở phần cuối.
pandas.concat: Hướng dẫn sử dụng còn thiếu
Liên kết đến tài liệu hiện tại
Nhập và xác định đối tượng
import pandas as pd
d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3])
s1 = pd.Series([1, 2], index=[2, 3])
s2 = pd.Series([3, 4], index=[1, 2])
s3 = pd.Series([5, 6], index=[1, 3])
Tranh luận
objs
Đối số đầu tiên mà chúng ta gặp phải là objs:
objs : một chuỗi hoặc ánh xạ của các đối tượng Series, DataFrame hoặc Panel Nếu một dict được chuyển, các khóa đã sắp xếp sẽ được sử dụng làm đối số khóa, trừ khi nó được truyền, trong trường hợp đó các giá trị sẽ được chọn (xem bên dưới). Mọi đối tượng Không có sẽ bị loại bỏ âm thầm trừ khi tất cả chúng đều Không có. Trong trường hợp đó, một ValueError sẽ được nâng lên
- Chúng tôi thường thấy điều này được sử dụng với một danh sách
Serieshoặc DataFramecác đối tượng.
- Tôi sẽ chỉ ra rằng điều đó
dictcũng có thể rất hữu ích.
- Máy phát điện cũng có thể được sử dụng và có thể hữu ích khi sử dụng
mapnhư trongmap(f, list_of_df)
Bây giờ, chúng ta sẽ gắn bó với danh sách một số đối tượng DataFramevà Seriesđối tượng được xác định ở trên. Tôi sẽ chỉ ra cách tận dụng từ điển để đưa ra những MultiIndexkết quả rất hữu ích sau này.
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
axis
Đối số thứ hai mà chúng ta gặp phải axiscó giá trị mặc định là 0:
trục : {0 / 'index', 1 / 'cột'}, mặc định là 0 Trục để nối với nhau.
Hai DataFrames với axis=0(xếp chồng lên nhau)
Đối với các giá trị của 0hoặc indexchúng tôi muốn nói: "Căn chỉnh dọc theo các cột và thêm vào chỉ mục".
Như được hiển thị ở trên nơi chúng tôi đã sử dụng axis=0, vì 0là giá trị mặc định và chúng tôi thấy rằng chỉ mục của d2mở rộng chỉ mục của d1mặc dù có sự chồng chéo của giá trị 2:
pd.concat([d1, d2], axis=0)
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
Hai DataFrames với axis=1(cạnh nhau)
Đối với các giá trị 1hoặc columnschúng tôi muốn nói: "Căn chỉnh theo chỉ mục và thêm vào các cột",
pd.concat([d1, d2], axis=1)
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
Chúng ta có thể thấy rằng chỉ mục kết quả là sự kết hợp của các chỉ số và các cột kết quả là phần mở rộng của các cột từ d1các cột của d2.
Hai (hoặc Ba) Seriesvới axis=0(xếp chồng lên nhau)
Khi kết hợp pandas.Seriescùng nhau axis=0, chúng ta nhận được lại a pandas.Series. Tên của kết quả Seriessẽ là Nonetrừ khi tất cả Seriesđược kết hợp có cùng tên. Hãy chú ý đến thời 'Name: A'điểm chúng tôi in ra kết quả Series. Khi nó không có mặt, chúng ta có thể cho rằng Seriestên là None.
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('A'),
pd.concat( | [s1.rename('A'), | [s1.rename('A'), | s2.rename('B'),
[s1, s2]) | s2]) | s2.rename('A')]) | s3.rename('A')])
-------------- | --------------------- | ---------------------- | ----------------------
2 1 | 2 1 | 2 1 | 2 1
3 2 | 3 2 | 3 2 | 3 2
1 3 | 1 3 | 1 3 | 1 3
2 4 | 2 4 | 2 4 | 2 4
dtype: int64 | dtype: int64 | Name: A, dtype: int64 | 1 5
| | | 3 6
| | | dtype: int64
Hai (hoặc Ba) Seriesvới axis=1(cạnh nhau)
Khi kết hợp pandas.Seriescùng axis=1, nó là namethuộc tính mà chúng tôi tham chiếu đến để suy ra tên cột trong kết quả pandas.DataFrame.
| | pd.concat(
| pd.concat( | [s1.rename('X'),
pd.concat( | [s1.rename('X'), | s2.rename('Y'),
[s1, s2], axis=1) | s2], axis=1) | s3.rename('Z')], axis=1)
---------------------- | --------------------- | ------------------------------
0 1 | X 0 | X Y Z
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 5.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 NaN
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN 6.0
Hỗn hợp Seriesvà DataFramevới axis=0(xếp chồng lên nhau)
Khi thực hiện nối a Seriesvà DataFramecùng axis=0, chúng ta chuyển tất cả Seriesthành cột đơn DataFrames.
Hãy đặc biệt lưu ý rằng đây là một nối cùng axis=0; điều đó có nghĩa là mở rộng chỉ mục (hàng) trong khi căn chỉnh các cột. Trong các ví dụ dưới đây, chúng ta thấy chỉ số trở thành [2, 3, 2, 3]một chỉ số nối tiếp một cách bừa bãi. Các cột không chồng lên nhau trừ khi tôi buộc đặt tên của Seriescột với đối số là to_frame:
pd.concat( |
[s1.to_frame(), d1]) | pd.concat([s1, d1])
------------------------- | ---------------------
0 A B C | 0 A B C
2 1.0 NaN NaN NaN | 2 1.0 NaN NaN NaN
3 2.0 NaN NaN NaN | 3 2.0 NaN NaN NaN
2 NaN 0.1 0.2 0.3 | 2 NaN 0.1 0.2 0.3
3 NaN 0.1 0.2 0.3 | 3 NaN 0.1 0.2 0.3
Bạn có thể thấy kết quả của pd.concat([s1, d1])giống như thể tôi đã to_frametự làm sạch.
Tuy nhiên, tôi có thể kiểm soát tên của cột kết quả với một tham số là to_frame. Đổi tên Seriesvới renamephương thức không kiểm soát tên cột trong kết quả DataFrame.
# Effectively renames | |
# `s1` but does not align | # Does not rename. So | # Renames to something
# with columns in `d1` | # Pandas defaults to `0` | # that does align with `d1`
pd.concat( | pd.concat( | pd.concat(
[s1.to_frame('X'), d1]) | [s1.rename('X'), d1]) | [s1.to_frame('B'), d1])
---------------------------- | -------------------------- | ----------------------------
A B C X | 0 A B C | A B C
2 NaN NaN NaN 1.0 | 2 1.0 NaN NaN NaN | 2 NaN 1.0 NaN
3 NaN NaN NaN 2.0 | 3 2.0 NaN NaN NaN | 3 NaN 2.0 NaN
2 0.1 0.2 0.3 NaN | 2 NaN 0.1 0.2 0.3 | 2 0.1 0.2 0.3
3 0.1 0.2 0.3 NaN | 3 NaN 0.1 0.2 0.3 | 3 0.1 0.2 0.3
Hỗn hợp Seriesvà DataFramevới axis=1(cạnh nhau)
Điều này là khá trực quan. Seriestên cột mặc định là danh sách các Seriesđối tượng như vậy khi namekhông có thuộc tính.
| pd.concat(
pd.concat( | [s1.rename('X'),
[s1, d1], | s2, s3, d1],
axis=1) | axis=1)
------------------- | -------------------------------
0 A B C | X 0 1 A B C
2 1 0.1 0.2 0.3 | 1 NaN 3.0 5.0 NaN NaN NaN
3 2 0.1 0.2 0.3 | 2 1.0 4.0 NaN 0.1 0.2 0.3
| 3 2.0 NaN 6.0 0.1 0.2 0.3
join
Đối số thứ ba joinmô tả liệu hợp nhất kết quả nên là hợp nhất bên ngoài (mặc định) hay hợp nhất bên trong.
join : {'inside', 'external'}, default 'external'
Cách xử lý các chỉ mục trên (các) trục khác.
Hóa ra, không có lefthoặc righttùy chọn pd.concatcó thể xử lý nhiều hơn hai đối tượng để hợp nhất.
Trong trường hợp d1và d2, các tùy chọn trông giống như:
outer
pd.concat([d1, d2], axis=1, join='outer')
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
inner
pd.concat([d1, d2], axis=1, join='inner')
A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6
join_axes
Đối số thứ tư là thứ cho phép chúng tôi thực hiện lefthợp nhất và hơn thế nữa.
join_axes : danh sách các đối tượng Chỉ mục Chỉ mục
cụ thể để sử dụng cho n - 1 trục khác thay vì thực hiện logic tập hợp bên trong / bên ngoài.
Hợp nhất trái
pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index])
A B C B C D A B D
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
Hợp nhất bên phải
pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index])
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
ignore_index
ignore_index : boolean, mặc định Sai
Nếu Đúng, không sử dụng các giá trị chỉ mục dọc theo trục nối. Trục kết quả sẽ được gắn nhãn 0, ..., n - 1. Điều này rất hữu ích nếu bạn đang nối các đối tượng mà trục nối không có thông tin chỉ mục có ý nghĩa. Lưu ý rằng các giá trị chỉ mục trên các trục khác vẫn được tôn trọng trong phép nối.
Giống như khi tôi xếp chồng d1lên nhau d2, nếu tôi không quan tâm đến các giá trị chỉ mục, tôi có thể đặt lại chúng hoặc bỏ qua chúng.
| pd.concat( | pd.concat(
| [d1, d2], | [d1, d2]
pd.concat([d1, d2]) | ignore_index=True) | ).reset_index(drop=True)
--------------------- | ----------------------- | -------------------------
A B C D | A B C D | A B C D
2 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6
Và khi sử dụng axis=1:
| pd.concat(
| [d1, d2], axis=1,
pd.concat([d1, d2], axis=1) | ignore_index=True)
------------------------------- | -------------------------------
A B C B C D | 0 1 2 3 4 5
1 NaN NaN NaN 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 3 0.1 0.2 0.3 NaN NaN NaN
keys
Chúng ta có thể chuyển một danh sách các giá trị vô hướng hoặc các bộ giá trị để gán các bộ giá trị hoặc bộ giá trị vô hướng cho MultiIndex tương ứng. Độ dài của danh sách đã thông qua phải bằng độ dài với số mục được nối.
các phím : trình tự, mặc định Không có
Nếu nhiều cấp được vượt qua, phải chứa các bộ giá trị. Xây dựng chỉ mục phân cấp bằng cách sử dụng các khóa được truyền làm cấp ngoài cùng
axis=0
Khi nối Seriescác đối tượng dọc theo axis=0(mở rộng chỉ mục).
Các khóa đó, trở thành cấp ban đầu mới của một MultiIndexđối tượng trong thuộc tính chỉ mục.
# length 3 length 3 # length 2 length 2
# /--------\ /-----------\ # /----\ /------\
pd.concat([s1, s2, s3], keys=['A', 'B', 'C']) pd.concat([s1, s2], keys=['A', 'B'])
---------------------------------------------- -------------------------------------
A 2 1 A 2 1
3 2 3 2
B 1 3 B 1 3
2 4 2 4
C 1 5 dtype: int64
3 6
dtype: int64
Tuy nhiên, chúng ta có thể sử dụng nhiều hơn các giá trị vô hướng trong keys đối số để tạo ra một sâu hơn MultiIndex. Ở đây chúng ta vượt qua tuplesđộ dài 2 để thêm vào trước hai cấp độ mới của a MultiIndex:
pd.concat(
[s1, s2, s3],
keys=[('A', 'X'), ('A', 'Y'), ('B', 'X')])
-----------------------------------------------
A X 2 1
3 2
Y 1 3
2 4
B X 1 5
3 6
dtype: int64
axis=1
Nó hơi khác một chút khi mở rộng dọc theo các cột. Khi chúng tôi sử dụng axis=0(xem ở trên), chúng tôi keyshoạt động như MultiIndexcác cấp ngoài chỉ mục hiện có. Vì axis=1, chúng tôi đang đề cập đến một trục mà Seriescác đối tượng không có, đó là columnsthuộc tính.
Các biến thể của Two
Serieswtih
axis=1
Lưu ý rằng đặt tên cho s1và s2các vấn đề miễn là không keysđược thông qua, nhưng nó sẽ bị ghi đè nếu keysđược thông qua.
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('U'),
pd.concat( | [s1, s2], | [s1.rename('U'), | s2.rename('V')],
[s1, s2], | axis=1, | s2.rename('V')], | axis=1,
axis=1) | keys=['X', 'Y']) | axis=1) | keys=['X', 'Y'])
-------------- | --------------------- | ---------------------- | ----------------------
0 1 | X Y | U V | X Y
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN
MultiIndexvới
Seriesvà
axis=1
pd.concat(
[s1, s2],
axis=1,
keys=[('W', 'X'), ('W', 'Y')])
-----------------------------------
W
X Y
1 NaN 3.0
2 1.0 4.0
3 2.0 NaN
Hai
DataFramevới
axis=1
Như với các axis=0ví dụ, hãy keysthêm cấp độ vào a MultiIndex, nhưng lần này là đối tượng được lưu trữ trong columnsthuộc tính.
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=[('First', 'X'), ('Second', 'X')])
------------------------------- | --------------------------------------------
X Y | First Second
A B C B C D | X X
1 NaN NaN NaN 0.4 0.5 0.6 | A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
| 3 0.1 0.2 0.3 NaN NaN NaN
Seriesvà
DataFramevới
axis=1
Điều này là khó khăn. Trong trường hợp này, giá trị khóa vô hướng không thể hoạt động như cấp chỉ mục duy nhất cho Seriesđối tượng khi nó trở thành một cột trong khi cũng hoạt động như cấp đầu tiên của a MultiIndexcho DataFrame. Vì vậy, Pandas sẽ lại sử dụng namethuộc tính của Seriesđối tượng làm nguồn của tên cột.
pd.concat( | pd.concat(
[s1, d1], | [s1.rename('Z'), d1],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=['X', 'Y'])
--------------------- | --------------------------
X Y | X Y
0 A B C | Z A B C
2 1 0.1 0.2 0.3 | 2 1 0.1 0.2 0.3
3 2 0.1 0.2 0.3 | 3 2 0.1 0.2 0.3
Hạn chế của
keysvà suy luận
MultiIndex.
Con gấu trúc dường như chỉ suy ra tên cột từ Seriestên, nhưng nó sẽ không điền vào chỗ trống khi thực hiện nối tương tự giữa các khung dữ liệu có số lượng cấp cột khác nhau.
d1_ = pd.concat(
[d1], axis=1,
keys=['One'])
d1_
One
A B C
2 0.1 0.2 0.3
3 0.1 0.2 0.3
Sau đó, nối khung dữ liệu này với một khung dữ liệu khác chỉ có một cấp trong đối tượng cột và Pandas sẽ từ chối thử tạo các bộ giá trị của MultiIndexđối tượng và kết hợp tất cả các khung dữ liệu như thể một cấp độ duy nhất của các đối tượng, vô hướng và bộ giá trị.
pd.concat([d1_, d2], axis=1)
(One, A) (One, B) (One, C) B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
Vượt qua một dictthay vì mộtlist
Khi truyền từ điển, pandas.concatsẽ sử dụng các khóa từ từ điển làm keystham số.
# axis=0 | # axis=1
pd.concat( | pd.concat(
{0: d1, 1: d2}) | {0: d1, 1: d2}, axis=1)
----------------------- | -------------------------------
A B C D | 0 1
0 2 0.1 0.2 0.3 NaN | A B C B C D
3 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
levels
Điều này được sử dụng cùng với keysđối số. Khi levelsđược để làm giá trị mặc định của nó None, Pandas sẽ lấy các giá trị duy nhất của mỗi cấp của kết quả MultiIndexvà sử dụng nó làm đối tượng được sử dụng trong index.levelsthuộc tính kết quả .
cấp độ : danh sách trình tự, mặc định Không có Cấp
độ cụ thể (giá trị duy nhất) để sử dụng để tạo MultiIndex. Nếu không, chúng sẽ được suy ra từ các phím.
Nếu Pandas đã suy ra những cấp độ này thì có lợi gì để tự mình chỉ định nó? Tôi sẽ đưa ra một ví dụ và để bạn nghĩ ra những lý do khác tại sao điều này có thể hữu ích.
Thí dụ
Theo tài liệu, levelsđối số là một danh sách các chuỗi. Điều này có nghĩa là chúng ta có thể sử dụng cái khác pandas.Indexlàm một trong những chuỗi đó.
Hãy xem xét khung dữ liệu dflà kết nối của d1, d2và d3:
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'])
df
First Second Fourth
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
Các cấp độ của đối tượng cột là:
print(df, *df.columns.levels, sep='\n')
Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')
Nếu chúng tôi sử dụng sumtrong một, groupbychúng tôi nhận được:
df.groupby(axis=1, level=0).sum()
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
Nhưng điều gì sẽ xảy ra nếu thay vì ['First', 'Second', 'Fourth']có một danh mục bị thiếu khác được đặt tên Thirdvà Fifth? Và tôi muốn chúng được đưa vào kết quả của một groupbytổng hợp? Chúng tôi có thể làm điều này nếu chúng tôi có pandas.CategoricalIndex. Và chúng tôi có thể chỉ định điều đó trước vớilevels đối số.
Vì vậy, thay vào đó, hãy định nghĩa dflà:
cats = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True)
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'],
levels=[lvl]
)
df
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
Nhưng mức đầu tiên của đối tượng cột là:
df.columns.levels[0]
CategoricalIndex(
['First', 'Second', 'Third', 'Fourth', 'Fifth'],
categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
ordered=True, dtype='category')
Và groupbytổng kết của chúng tôi trông giống như:
df.groupby(axis=1, level=0).sum()
First Second Third Fourth Fifth
1 0.0 1.5 0.0 2.4 0.0
2 0.6 1.5 0.0 0.0 0.0
3 0.6 0.0 0.0 2.4 0.0
names
Điều này được sử dụng để đặt tên cho các mức của một kết quả MultiIndex. Độ dài của namesdanh sách phải khớp với số cấp trong kết quả MultiIndex.
tên : danh sách, mặc định Không có
Tên cho các cấp trong chỉ mục phân cấp kết quả
# axis=0 | # axis=1
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
keys=[0, 1], | axis=1, keys=[0, 1],
names=['lvl0', 'lvl1']) | names=['lvl0', 'lvl1'])
----------------------------- | ----------------------------------
A B C D | lvl0 0 1
lvl0 lvl1 | lvl1 A B C B C D
0 2 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
2 NaN 0.4 0.5 0.6 |
verify_integrity
Tài liệu tự giải thích
verify_integrity : boolean, default False
Kiểm tra xem trục nối mới có chứa các bản sao không. Điều này có thể rất tốn kém so với việc ghép dữ liệu thực tế.
Bởi vì chỉ mục kết quả từ việc nối d1và d2không phải là duy nhất, nó sẽ không kiểm tra được tính toàn vẹn.
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
Và
pd.concat([d1, d2], verify_integrity=True)
> ValueError: Các chỉ mục có các giá trị chồng chéo: [2]