Spark DataFrame 열을 Python 목록으로 변환


104

두 개의 열, mvv 및 count가있는 데이터 프레임에서 작업합니다.

+---+-----+
|mvv|count|
+---+-----+
| 1 |  5  |
| 2 |  9  |
| 3 |  3  |
| 4 |  1  |

mvv 값과 카운트 값을 포함하는 두 개의 목록을 얻고 싶습니다. 같은 것

mvv = [1,2,3,4]
count = [5,9,3,1]

그래서 다음 코드를 시도했습니다. 첫 번째 줄은 파이썬 행 목록을 반환해야합니다. 첫 번째 값을보고 싶었습니다.

mvv_list = mvv_count_df.select('mvv').collect()
firstvalue = mvv_list[0].getInt(0)

하지만 두 번째 줄에 오류 메시지가 나타납니다.

AttributeError : getInt


Spark 2.3부터이 코드는 가장 빠르고 OutOfMemory 예외를 유발할 가능성이 가장 적습니다 list(df.select('mvv').toPandas()['mvv']).. Arrow는 PySpark에 통합되어toPandas 상당히 빨라졌습니다 . Spark 2.3 이상을 사용하는 경우 다른 접근 방식을 사용하지 마십시오. 자세한 벤치마킹 세부 정보는 내 대답을 참조하십시오.
Powers

답변:


141

왜 이런 식으로 작동하지 않는지보십시오. 먼저 유형 에서 정수를 가져 오려고 합니다. 수집 결과는 다음과 같습니다.

>>> mvv_list = mvv_count_df.select('mvv').collect()
>>> mvv_list[0]
Out: Row(mvv=1)

다음과 같은 것을 취하면 :

>>> firstvalue = mvv_list[0].mvv
Out: 1

당신은 mvv가치 를 얻을 것 입니다. 배열의 모든 정보를 원하면 다음과 같이 할 수 있습니다.

>>> mvv_array = [int(row.mvv) for row in mvv_list.collect()]
>>> mvv_array
Out: [1,2,3,4]

그러나 다른 열에 대해 동일하게 시도하면 다음과 같은 결과가 나타납니다.

>>> mvv_count = [int(row.count) for row in mvv_list.collect()]
Out: TypeError: int() argument must be a string or a number, not 'builtin_function_or_method'

이것은 count내장 메서드 이기 때문에 발생합니다 . 그리고 열의 이름은 count. 이를위한 해결 방법은 열 이름을 다음 count_count같이 변경하는 것입니다 .

>>> mvv_list = mvv_list.selectExpr("mvv as mvv", "count as _count")
>>> mvv_count = [int(row._count) for row in mvv_list.collect()]

그러나 사전 구문을 사용하여 열에 액세스 할 수 있으므로이 해결 방법은 필요하지 않습니다.

>>> mvv_array = [int(row['mvv']) for row in mvv_list.collect()]
>>> mvv_count = [int(row['count']) for row in mvv_list.collect()]

그리고 마침내 작동합니다!


그것은 첫 번째 열에 위대한 작품,하지만 난 때문에 (스파크의 기능 수) 생각 카운트 열이 작동하지 않습니다
a.moussa

카운트로 무엇을하고 있는지 추가 할 수 있습니까? 여기에 의견을 추가하십시오.
Thiago Baldim

응답 해 주셔서 감사합니다. 그래서이 줄은 mvv_count.select ( 'mvv'). collect ()]에서 i에 대해 mvv_list = [int (i.mvv) 작동하지만 mvv_count에서 i에 대해 count_list = [int (i.count)]가 아닙니다. .select ( 'count'). collect ()] 잘못된 구문 반환
a.moussa

다음과 같이이 select('count')용도 를 추가 할 필요는 없습니다 count_list = [int(i.count) for i in mvv_list.collect()]. 응답에 예제를 추가하겠습니다.
Thiago Baldim

1
@ a.moussa [i.['count'] for i in mvv_list.collect()]count함수가 아닌 'count'라는 열을 사용하도록 명시 적으로 작업 합니다.
user989762

103

한 줄을 따라 가면 원하는 목록을 얻을 수 있습니다.

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()

3
성능면에서이 솔루션은 솔루션보다 훨씬 빠릅니다. mvv_list = [int (i.mvv) for i in mvv_count.select ( 'mvv'). collect ()]
Chanaka Fernando

이것은 내가 본 최고의 솔루션입니다. 감사.
hui chen 1911

22

이렇게하면 모든 요소가 목록으로 제공됩니다.

mvv_list = list(
    mvv_count_df.select('mvv').toPandas()['mvv']
)

1
이것은 Spark 2.3+를위한 가장 빠르고 효율적인 솔루션입니다. 내 답변에서 벤치마킹 결과를 참조하십시오.
Powers

15

다음 코드가 도움이 될 것입니다.

mvv_count_df.select('mvv').rdd.map(lambda row : row[0]).collect()

3
이것은 받아 들여진 대답이어야합니다. 그 이유는 프로세스 전반에 걸쳐 스파크 컨텍스트에 머물고 있으며 수행중인 작업에 따라 더 큰 수집이 발생할 수있는 더 일찍 스파크 컨텍스트에서 벗어나는 것과 반대로 마지막에 수집하기 때문입니다.
AntiPawn79

15

내 데이터에서 다음과 같은 벤치 마크를 얻었습니다.

>>> data.select(col).rdd.flatMap(lambda x: x).collect()

0.52 초

>>> [row[col] for row in data.collect()]

0.271 초

>>> list(data.select(col).toPandas()[col])

0.427 초

결과는 동일합니다


1
toLocalIterator대신 사용 하는 경우 collect메모리 효율성이 더 [row[col] for row in data.toLocalIterator()]
높아야

5

아래 오류가 발생하는 경우 :

AttributeError : 'list'개체에 'collect'속성이 없습니다.

이 코드는 문제를 해결합니다.

mvv_list = mvv_count_df.select('mvv').collect()

mvv_array = [int(i.mvv) for i in mvv_list]

나도 그 오류가 있었고이 솔루션이 문제를 해결했습니다. 하지만 왜 오류가 발생 했습니까? (많은 다른 사람들은 그것을 이해하지 못하는 것 같습니다!)
bikashg

1

벤치마킹 분석을 실행했고 list(mvv_count_df.select('mvv').toPandas()['mvv']) 했고 가장 빠른 방법입니다. 나는 매우 놀랐다.

Spark 2.4.5와 함께 5 노드 i3.xlarge 클러스터 (각 노드에는 30.5GB의 RAM과 4 코어가 있음)를 사용하여 10 만 / 1 억 행 데이터 세트에 대해 다른 접근 방식을 실행했습니다. 데이터는 단일 열이있는 20 개의 깔끔한 압축 Parquet 파일에 균등하게 배포되었습니다.

벤치마킹 결과 (실행 시간 (초))는 다음과 같습니다.

+-------------------------------------------------------------+---------+-------------+
|                          Code                               | 100,000 | 100,000,000 |
+-------------------------------------------------------------+---------+-------------+
| df.select("col_name").rdd.flatMap(lambda x: x).collect()    |     0.4 | 55.3        |
| list(df.select('col_name').toPandas()['col_name'])          |     0.4 | 17.5        |
| df.select('col_name').rdd.map(lambda row : row[0]).collect()|     0.9 | 69          |
| [row[0] for row in df.select('col_name').collect()]         |     1.0 | OOM         |
| [r[0] for r in mid_df.select('col_name').toLocalIterator()] |     1.2 | *           |
+-------------------------------------------------------------+---------+-------------+

* cancelled after 800 seconds

드라이버 노드에서 데이터를 수집 할 때 따라야 할 황금률 :

  • 다른 접근 방식으로 문제를 해결하십시오. 드라이버 노드에 데이터를 수집하는 것은 비용이 많이 들고 Spark 클러스터의 성능을 활용하지 않으며 가능한 한 피해야합니다.
  • 가능한 한 적은 수의 행을 수집하십시오. 데이터를 수집하기 전에 열을 집계, 중복 제거, 필터링 및 정리합니다. 가능한 한 적은 데이터를 드라이버 노드에 보냅니다.

toPandas Spark 2.3에서 크게 개선되었습니다. . 2.3 이전의 Spark 버전을 사용하는 경우 최선의 방법이 아닐 수 있습니다.

자세한 내용 / 벤치마킹 결과는 여기 를 참조 하십시오 .


1

가능한 해결책은 사용 collect_list()에서 기능 pyspark.sql.functions. 이렇게하면 모든 열 값이 수집 될 때 python 목록으로 변환되는 pyspark 배열로 집계됩니다.

mvv_list   = df.select(collect_list("mvv")).collect()[0][0]
count_list = df.select(collect_list("count")).collect()[0][0] 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.