Spark DataFrame sütununu python listesine dönüştür


109

İki sütun, mvv ve sayım içeren bir veri çerçevesi üzerinde çalışıyorum.

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

mvv değerleri ve count değeri içeren iki liste elde etmek istiyorum. Gibi bir şey

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

Bu yüzden, aşağıdaki kodu denedim: İlk satır bir python satırı listesi döndürmelidir. İlk değeri görmek istedim:

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

Ancak ikinci satırda bir hata mesajı alıyorum:

AttributeError: getInt


Spark 2.3 itibariyle bu kod en hızlı ve en muhtemel OutOfMemory istisnalar neden: list(df.select('mvv').toPandas()['mvv']). Ok PySpark entegre edilmiş hızlandırdı toPandasanlamlı. Spark 2.3+ kullanıyorsanız diğer yaklaşımları kullanmayın. Daha fazla kıyaslama ayrıntısı için cevabıma bakın.
Powers

Yanıtlar:


148

Gördün mü, neden böyle yapıyorsun işe yaramıyor. İlk olarak, bir Satır Tipinden tamsayı almaya çalışıyorsunuz , koleksiyonunuzun çıktısı şu şekildedir :

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

Böyle bir şey alırsan:

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

mvvDeğeri alacaksın . Dizinin tüm bilgilerini istiyorsanız, şöyle bir şey alabilirsiniz:

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

Ancak diğer sütun için de aynısını denerseniz, şunu elde edersiniz:

>>> 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'

Bunun nedeni countyerleşik bir yöntem olmasıdır. Ve sütun ile aynı ada sahip count. Bunu yapmanın bir geçici çözüm sütun adını değiştirmek olduğunu countiçin _count:

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

Ancak, sütuna sözlük sözdizimini kullanarak erişebileceğiniz için bu geçici çözüm gerekli değildir:

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

Ve sonunda işe yarayacak!


ilk sütun için harika çalışıyor, ancak sütun sayısı için çalışmıyor (kıvılcımın işlev sayısı)
a.moussa

Sayıma ne yaptığını ekleyebilir misin? Yorumlara buraya ekleyin.
Thiago Baldim

Cevabınız için teşekkürler Yani bu satır mvv_list = [int (i.mvv) i in mvv_count.select ('mvv'). Collect ()] olarak çalışır ama bu bir count_list = [int (i.count) mvv_count i için değil .select ('count']. Collect ()] geçersiz sözdizimi
döndür

Bu select('count')kullanımı şu şekilde eklemenize gerek yok : count_list = [int(i.count) for i in mvv_list.collect()]Örneği yanıta ekleyeceğim.
Thiago Baldim

1
@ a.moussa [i.['count'] for i in mvv_list.collect()], countişlevi değil, 'say' adlı sütunu kullanmayı açık hale getirmek için çalışıyor
user989762

110

Bir satırı takip etmek istediğiniz listeyi verir.

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

3
Performans açısından bu çözüm, mvv_count.select ('mvv') içinde i için mvv_list = [int (i.mvv) çözümünüzden çok daha hızlıdır. Collect ()]
Chanaka Fernando

Bu şimdiye kadar gördüğüm en iyi çözüm. Teşekkürler.
hui chen

Bu sadece OP'nin sorusu için işe yaramaz mı ?: mvv = mvv_count_df.select ("mvv"). Rdd.flatMap (liste) .collect ()
eemilk

23

Bu size tüm unsurları bir liste halinde verecektir.

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

1
Bu, Spark 2.3+ için en hızlı ve en verimli çözümdür. Cevabımda karşılaştırma sonuçlarını görün.
Powers

19

Aşağıdaki kod size yardımcı olacaktır

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

3
Kabul edilen cevap bu olmalıdır. Bunun nedeni, işlem boyunca bir kıvılcım bağlamında kalıyor olmanız ve daha sonra kıvılcım bağlamından daha erken çıkmanın aksine, ne yaptığınıza bağlı olarak daha büyük bir toplamaya neden olabilecek şekilde sonunda toplarsınız.
AntiPawn79

16

Verilerimde şu kıyaslamaları aldım:

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

0.52 saniye

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

0.271 saniye

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

0.427 saniye

Sonuç aynı


2
Onun toLocalIteratoryerine kullanırsanız, collectdaha da verimli bellek gerekir[row[col] for row in data.toLocalIterator()]
oglop

Bahşiş için teşekkürler! @o
Andre Carneiro

6

Aşağıdaki hatayı alırsanız:

AttributeError: 'list' nesnesinin 'Collect' özniteliği yok

Bu kod sorunlarınızı çözecektir:

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

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

Ben de bu hatayı aldım ve bu çözüm sorunu çözdü. Ama neden hatayı aldım? (Diğerleri bunu anlamıyor!)
bikashg

5

Bir kıyaslama analizi yaptım ve list(mvv_count_df.select('mvv').toPandas()['mvv'])en hızlı yöntem bu. Ben çok şaşırdım

Spark 2.4.5 ile 5 düğümlü bir i3.xlarge kümesi (her düğüm 30.5 GB RAM ve 4 çekirdek vardır) kullanarak 100 bin / 100 milyon satır veri kümesinde farklı yaklaşımları çalıştırdım. Veriler, tek bir sütunlu 20 hızlı sıkıştırılmış Parquet dosyasına eşit olarak dağıtıldı.

İşte kıyaslama sonuçları (saniye cinsinden çalışma süreleri):

+-------------------------------------------------------------+---------+-------------+
|                          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

Sürücü düğümünde veri toplarken uyulması gereken altın kurallar:

  • Sorunu başka yaklaşımlarla çözmeye çalışın. Sürücü düğümüne veri toplamak pahalıdır, Spark kümesinin gücünden yararlanmaz ve mümkün olduğunda bundan kaçınılmalıdır.
  • Mümkün olduğunca az sıra toplayın. Verileri toplamadan önce sütunları toplayın, tekilleştirin, filtreleyin ve budayın. Sürücü düğümüne olabildiğince az veri gönderin.

toPandas Spark 2.3'te önemli ölçüde geliştirildi . 2.3'ten önceki bir Spark sürümünü kullanıyorsanız muhtemelen en iyi yaklaşım bu değildir.

Daha fazla ayrıntı / karşılaştırma sonuçları için buraya bakın .


2

Olası bir çözüm, collect_list()işlevini kullanıyor pyspark.sql.functions. Bu, tüm sütun değerlerini, toplandığında bir python listesine dönüştürülen bir pyspark dizisinde toplayacaktır:

mvv_list   = df.select(collect_list("mvv")).collect()[0][0]
count_list = df.select(collect_list("count")).collect()[0][0] 

1

Söz konusu veri çerçevesini oluşturalım

df_test = spark.createDataFrame(
    [
        (1, 5),
        (2, 9),
        (3, 3),
        (4, 1),
    ],
    ['mvv', 'count']
)
df_test.show()

Hangi verir

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

ve sonra listeyi almak için rdd.flatMap (f) .collect () uygulayın

test_list = df_test.select("mvv").rdd.flatMap(list).collect()
print(type(test_list))
print(test_list)

hangi verir

<type 'list'>
[1, 2, 3, 4]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.