चिंगारी DataFrame कॉलम को अजगर सूची में बदलें


104

मैं दो कॉलम, mvv और काउंट के साथ एक डेटाफ्रेम पर काम करता हूं।

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

लेकिन मुझे दूसरी पंक्ति के साथ एक त्रुटि संदेश मिलता है:

विशेषता: getInt


स्पार्क 2.3 के रूप में, यह कोड सबसे तेज़ और कम से कम आउटऑफ़मेरी अपवादों के कारण होने की संभावना है list(df.select('mvv').toPandas()['mvv']):। एरो को PySpark में एकीकृत किया गया था, जिसमें toPandasकाफी वृद्धि हुई थी। यदि आप स्पार्क 2.3+ का उपयोग कर रहे हैं तो अन्य तरीकों का उपयोग न करें। अधिक बेंचमार्किंग विवरण के लिए मेरा उत्तर देखें।
१४:०४ पर

जवाबों:


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

क्या आप जोड़ सकते हैं कि आप गिनती के साथ क्या कर रहे हैं? टिप्पणियों में यहां जोड़ें।
थियागो बाल्डिम

आपकी प्रतिक्रिया के लिए धन्यवाद तो यह लाइन mvv_list = [int (i.mvv) के लिए i में mvv_count.select ('mvv') इकट्ठा करें ()] लेकिन यह नहीं के लिए मैं mvv_count में = count_list = [int (i.count)। .select ('count') इकट्ठा करें ()] अमान्य सिंटैक्स लौटें
a.moussa

इस select('count')उपयोग को इस तरह जोड़ने की आवश्यकता नहीं है : count_list = [int(i.count) for i in mvv_list.collect()]मैं उदाहरण को प्रतिक्रिया में जोड़ दूंगा।
थियागो बाल्डिम

1
@ a.moussa [i.['count'] for i in mvv_list.collect()]'काउंट' नाम के कॉलम का उपयोग करने के लिए इसे स्पष्ट करने का काम करता है, न कि countफ़ंक्शन का
user989762

103

एक लाइनर का पालन करने के बाद आप जो सूची चाहते हैं।

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

3
प्रदर्शन वार यह समाधान आपके समाधान mvv_list = [int (i.mvv) के लिए i mvv_count.select ('mvv') में एकत्र करने से बहुत तेज है। इकट्ठा ()]
Chanaka फर्नांडो

यह अब तक का सबसे अच्छा समाधान है जो मैंने देखा है। धन्यवाद।
हुइ चेन

22

यह आपको सूची के रूप में सभी तत्व देगा।

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

1
यह स्पार्क 2.3+ के लिए सबसे तेज़ और सबसे कुशल समाधान है। मेरे उत्तर में बेंचमार्किंग परिणाम देखें।
शक्तियां

16

निम्नलिखित कोड आपकी सहायता करेगा

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

3
यह स्वीकृत उत्तर होना चाहिए। इसका कारण यह है कि आप पूरी प्रक्रिया में एक स्पार्क संदर्भ में रह रहे हैं और फिर आप अंत में उस स्पार्क संदर्भ से बाहर निकलने के लिए एकत्र होते हैं जो पहले आप क्या कर रहे हैं उसके आधार पर एक बड़ा संग्रह हो सकता है।
एंटीपॉन 79

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()]
oglop

6

यदि आपको नीचे त्रुटि मिलती है:

गुण: 'सूची' ऑब्जेक्ट में कोई विशेषता नहीं 'संग्रह' है

यह कोड आपके मुद्दों को हल करेगा:

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

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

मुझे वह त्रुटि भी मिली और इस समाधान से समस्या हल हो गई। लेकिन मुझे त्रुटि क्यों मिली? (कई अन्य लोगों को लगता है कि नहीं मिलता है!)
bikashg

3

मैंने एक बेंचमार्किंग विश्लेषण चलाया और list(mvv_count_df.select('mvv').toPandas()['mvv'])यह सबसे तेज़ तरीका है। मैं बहुत हैरान हूं।

मैंने 5 नोड i3.xlarge क्लस्टर (प्रत्येक नोड में स्पार्क 2.4.5 के साथ 30.5 जीबी रैम और 4 कोर) का उपयोग करके 100 हजार / 100 मिलियन पंक्ति डेटासेट पर विभिन्न दृष्टिकोणों को चलाया। एक स्तंभ के साथ 20 तड़क-भड़क वाली संपीड़ित फ़ाइलों पर डेटा समान रूप से वितरित किया गया था।

यहां बेंचमार्किंग परिणाम (सेकंड में रनटाइम):

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

ड्राइवर नोड पर डेटा एकत्र करते समय अनुसरण करने के सुनहरे नियम:

  • अन्य दृष्टिकोणों के साथ समस्या को हल करने का प्रयास करें। ड्राइवर नोड के लिए डेटा एकत्र करना महंगा है, स्पार्क क्लस्टर की शक्ति का दोहन नहीं करता है, और जब भी संभव हो इसे टाला जाना चाहिए।
  • संभव के रूप में कुछ पंक्तियों को ले लीजिए। डेटा एकत्र करने से पहले एग्रीगेट, डुप्लीकेट, फ़िल्टर और प्रून कॉलम। जितना संभव हो उतना कम डेटा ड्राइवर नोड में भेजें।

toPandas स्पार्क 2.3 में काफी सुधार हुआ था । यदि आप 2.3 से पहले स्पार्क संस्करण का उपयोग कर रहे हैं तो शायद यह सबसे अच्छा तरीका नहीं है।

अधिक विवरण / बेंचमार्किंग परिणामों के लिए यहां देखें ।


2

एक संभावित समाधान collect_list()से फ़ंक्शन का उपयोग कर रहा है pyspark.sql.functions। यह सभी स्तंभ मानों को एक pyspark सरणी में एकत्रित करेगा, जिसे एकत्र करते समय एक अजगर सूची में परिवर्तित किया जाता है:

mvv_list   = df.select(collect_list("mvv")).collect()[0][0]
count_list = df.select(collect_list("count")).collect()[0][0] 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.