Khung dữ liệu Spark phân biệt các cột có tên trùng lặp


82

Vì vậy, như tôi biết trong Spark Dataframe, cho nhiều cột có thể có cùng tên như được hiển thị trong ảnh chụp nhanh khung dữ liệu bên dưới:

[
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=125231, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0047, 3: 0.0, 4: 0.0043})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=145831, f=SparseVector(5, {0: 0.0, 1: 0.2356, 2: 0.0036, 3: 0.0, 4: 0.4132})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=147031, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=149231, f=SparseVector(5, {0: 0.0, 1: 0.0032, 2: 0.2451, 3: 0.0, 4: 0.0042}))
]

Kết quả trên được tạo bằng cách nối với khung dữ liệu với chính nó, bạn có thể thấy có 4các cột có cả hai af.

Vấn đề là ở đó khi tôi cố gắng thực hiện nhiều phép tính hơn với acột, tôi không thể tìm cách chọn a, tôi đã thử df[0]df.select('a')cả hai đều trả lại cho tôi thông báo lỗi bên dưới:

AnalysisException: Reference 'a' is ambiguous, could be: a#1333L, a#1335L.

Có cách nào trong Spark API mà tôi có thể phân biệt lại các cột với các tên trùng lặp không? hoặc có thể một số cách để cho phép tôi thay đổi tên cột?

Câu trả lời:


61

Tôi khuyên bạn nên thay đổi tên cột cho của bạn join.

df1.select(col("a") as "df1_a", col("f") as "df1_f")
   .join(df2.select(col("a") as "df2_a", col("f") as "df2_f"), col("df1_a" === col("df2_a"))

Kết quả DataFramesẽ cóschema

(df1_a, df1_f, df2_a, df2_f)

5
Bạn có thể cần sửa câu trả lời của mình vì dấu ngoặc kép không được điều chỉnh đúng cách giữa các tên cột.
Sameh Sharaf

2
@SamehShara Nếu tôi cho rằng bạn là người bỏ phiếu cho câu trả lời của tôi? Nhưng câu trả lời trên thực tế là đúng 100% - tôi chỉ đơn giản là sử dụng tỷ 'lệ -shorthand để chọn cột, vì vậy trên thực tế không có vấn đề gì với dấu ngoặc kép.
Glennie Helles Sindholt,

31
@GlennieHellesSindholt, điểm công bằng. Thật khó hiểu vì câu trả lời được gắn thẻ là pythonpyspark.
Jorge Leitao

Điều gì sẽ xảy ra nếu mỗi khung dữ liệu chứa hơn 100 cột và chúng ta chỉ cần đổi tên một tên cột giống nhau? Chắc chắn, không thể nhập thủ công tất cả các tên cột đó trong mệnh đề chọn
bikashg

6
Trong trường hợp đó, bạn có thể đi cùngdf1.withColumnRenamed("a", "df1_a")
Glennie Helles Sindholt

100

Hãy bắt đầu với một số dữ liệu:

from pyspark.mllib.linalg import SparseVector
from pyspark.sql import Row

df1 = sqlContext.createDataFrame([
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
    Row(a=125231, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0047, 3: 0.0, 4: 0.0043})),
])

df2 = sqlContext.createDataFrame([
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
])

Có một số cách bạn có thể tiếp cận vấn đề này. Trước hết, bạn có thể tham chiếu rõ ràng các cột trong bảng con bằng cách sử dụng các cột cha:

df1.join(df2, df1['a'] == df2['a']).select(df1['f']).show(2)

##  +--------------------+
##  |                   f|
##  +--------------------+
##  |(5,[0,1,2,3,4],[0...|
##  |(5,[0,1,2,3,4],[0...|
##  +--------------------+

Bạn cũng có thể sử dụng bí danh bảng:

from pyspark.sql.functions import col

df1_a = df1.alias("df1_a")
df2_a = df2.alias("df2_a")

df1_a.join(df2_a, col('df1_a.a') == col('df2_a.a')).select('df1_a.f').show(2)

##  +--------------------+
##  |                   f|
##  +--------------------+
##  |(5,[0,1,2,3,4],[0...|
##  |(5,[0,1,2,3,4],[0...|
##  +--------------------+

Cuối cùng, bạn có thể đổi tên các cột theo chương trình:

df1_r = df1.select(*(col(x).alias(x + '_df1') for x in df1.columns))
df2_r = df2.select(*(col(x).alias(x + '_df2') for x in df2.columns))

df1_r.join(df2_r, col('a_df1') == col('a_df2')).select(col('f_df1')).show(2)

## +--------------------+
## |               f_df1|
## +--------------------+
## |(5,[0,1,2,3,4],[0...|
## |(5,[0,1,2,3,4],[0...|
## +--------------------+

7
Cảm ơn sự chỉnh sửa của bạn vì đã chỉ ra rất nhiều cách để lấy đúng cột trong những trường hợp mơ hồ đó, tôi nghĩ các ví dụ của bạn nên đi vào hướng dẫn lập trình Spark. Tôi đã học được rất nhiều!
resec

sửa nhỏ: df2_r = **df2** .select(*(col(x).alias(x + '_df2') for x in df2.columns))thay vì df2_r = df1.select(*(col(x).alias(x + '_df2') for x in df2.columns)). Đối với phần còn lại, công cụ tốt
Vzzarr

Tôi đồng ý với điều này nên là một phần của hướng dẫn lập trình Spark. Vàng nguyên chất. Cuối cùng tôi đã có thể gỡ rối nguồn gốc của sự mơ hồ khi chọn các cột theo tên cũ trước khi thực hiện phép nối. Giải pháp nối các hậu tố theo chương trình vào tên của các cột trước khi thực hiện việc nối tất cả những điều không rõ ràng.
Pablo Adames

26

Có một cách đơn giản hơn là viết bí danh cho tất cả các cột bạn đang tham gia bằng cách thực hiện:

df1.join(df2,['a'])

Điều này hoạt động nếu khóa bạn đang tham gia giống nhau trong cả hai bảng.

Xem https://kb.databricks.com/data/join-two-dataframes-duplicated-columns.html


4
đây là câu trả lời thực tế kể từ Spark 2+
Matt

2
Và cho Scala: df1.join (df2, Seq ("a"))
mauriciojost

1
Trang đã được chuyển đến: kb.databricks.com/data/...
bogdan.rusu

7

Bạn có thể sử dụng def drop(col: Column)phương pháp để loại bỏ cột trùng lặp, ví dụ:

DataFrame:df1

+-------+-----+
| a     | f   |
+-------+-----+
|107831 | ... |
|107831 | ... |
+-------+-----+

DataFrame:df2

+-------+-----+
| a     | f   |
+-------+-----+
|107831 | ... |
|107831 | ... |
+-------+-----+

khi tôi nối df1 với df2, DataFrame sẽ như sau:

val newDf = df1.join(df2,df1("a")===df2("a"))

DataFrame:newDf

+-------+-----+-------+-----+
| a     | f   | a     | f   |
+-------+-----+-------+-----+
|107831 | ... |107831 | ... |
|107831 | ... |107831 | ... |
+-------+-----+-------+-----+

Bây giờ, chúng ta có thể sử dụng def drop(col: Column)phương pháp để thả cột trùng lặp 'a' hoặc 'f', giống như sau:

val newDfWithoutDuplicate = df1.join(df2,df1("a")===df2("a")).drop(df2("a")).drop(df2("f"))

Phương pháp này có hiệu quả không nếu bạn đang thực hiện một phép nối ngoài và hai cột có một số giá trị khác nhau?
prafi

Bạn có thể không muốn bỏ nếu các quan hệ khác nhau với cùng một lược đồ.
thebluephantom

5

Sau khi tìm hiểu kỹ về Spark API, tôi thấy trước tiên tôi có thể sử dụng aliasđể tạo bí danh cho khung dữ liệu ban đầu, sau đó tôi sử dụng withColumnRenamedđể đổi tên thủ công mọi cột trên bí danh, điều này sẽ thực hiện joinmà không gây ra trùng lặp tên cột.

Chi tiết hơn có thể tham khảo bên dưới API khung dữ liệu Spark :

pyspark.sql.DataFrame.alias

pyspark.sql.DataFrame.withColumnRename

Tuy nhiên, tôi nghĩ rằng đây chỉ là một cách giải quyết rắc rối và tự hỏi liệu có cách nào tốt hơn cho câu hỏi của tôi không.


4

Đây là cách chúng ta có thể nối hai Dataframe trên cùng một tên cột trong PySpark.

df = df1.join(df2, ['col1','col2','col3'])

Nếu bạn làm printSchema()sau điều này thì bạn có thể thấy rằng các cột trùng lặp đã bị loại bỏ.


3

Giả sử DataFrames bạn muốn tham gia là df1 và df2, và bạn đang nối chúng trên cột 'a', thì bạn có 2 phương pháp

Phương pháp 1

df1.join (df2, 'a', 'left_outer')

Đây là một phương pháp tuyệt vời và nó rất được khuyến khích.

Phương pháp 2

df1.join (df2, df1.a == df2.a, 'left_outer'). drop (df2.a)


1

Đây có thể không phải là cách tốt nhất, nhưng nếu bạn muốn đổi tên các cột trùng lặp (sau khi nối), bạn có thể làm như vậy bằng cách sử dụng hàm nhỏ này.

def rename_duplicate_columns(dataframe):
    columns = dataframe.columns
    duplicate_column_indices = list(set([columns.index(col) for col in columns if columns.count(col) == 2]))
    for index in duplicate_column_indices:
        columns[index] = columns[index]+'2'
    dataframe = dataframe.toDF(*columns)
    return dataframe

1

nếu chỉ có cột chính giống nhau trong cả hai bảng thì hãy thử sử dụng cách sau (Phương pháp 1):

left. join(right , 'key', 'inner')

chứ không phải bên dưới (cách tiếp cận 2):

left. join(right , left.key == right.key, 'inner')

Ưu điểm của việc sử dụng phương pháp 1:

  • 'chìa khóa' sẽ chỉ hiển thị một lần trong khung dữ liệu cuối cùng
  • cú pháp dễ sử dụng

Nhược điểm của việc sử dụng cách tiếp cận 1:

  • chỉ giúp với cột chính
  • Các tình huống, trong đó trường hợp kết hợp bên trái, nếu dự định sử dụng phím bên phải đếm null, điều này sẽ không hoạt động. Trong trường hợp đó, người ta phải đổi tên một trong các khóa như đã đề cập ở trên.

0

Nếu bạn có một trường hợp sử dụng phức tạp hơn được mô tả trong câu trả lời của Glennie Helles Sindholt, ví dụ: bạn có / một số tên cột không nối khác cũng giống nhau và muốn phân biệt chúng trong khi chọn thì tốt nhất nên sử dụng bí danh, ví dụ:

df3 = df1.select("a", "b").alias("left")\
   .join(df2.select("a", "b").alias("right"), ["a"])\
   .select("left.a", "left.b", "right.b")

df3.columns
['a', 'b', 'b']
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.