本网站上有很多关于如何将pyspark rdd转换为数据帧的问题。但是他们都没有回答如何在保留类型的同时将SQL表样式rdd转换为数据帧的问题。
我有一个rdd,它正是python中的dicts列表:
>>> rdd.take(1)
[{'se_error': 0, 'se_subjective_count': 0, 'se_word_count': 10, 'se_entity_summary_topic_phrases': {}, 'se_entity_hits': 1, 'se_entity_summary': 'rt @mercuryinrx: disgusting. cut it out FOCALENTITY twitter.com/anons4cetacean', 'se_query_with_hits': 0, 'id': 180034992495.0, 'se_objective_count': 2, 'se_category': {}, 'se_sentence_count': 2, 'se_entity_sentiment': 0.0, 'se_document_sentiment': -0.49000000953674316, 'se_entity_themes': {}, 'se_query_hits': 0, 'se_named_entities': {}}]
>>> rdd.take(1)[0].keys()
dict_keys(['se_error', 'se_subjective_count', 'se_word_count', 'se_entity_summary_topic_phrases', 'se_entity_hits', 'se_entity_summary', 'se_query_with_hits', 'id', 'se_objective_count', 'se_category', 'se_sentence_count', 'se_entity_sentiment', 'se_document_sentiment', 'se_entity_themes', 'se_query_hits', 'se_named_entities'])
所有行都具有相同的列。所有列都具有相同的数据类型。在pandas中变成数据帧是微不足道的。
out = rdd.take(rdd.count())
outdf = pd.DataFrame(out)
这当然违背了使用火花的目的!我可以证明列也是相同的数据类型。
>>> typemap = [{key: type(val) for key, val in row.items()} for row in out]
>>> typedf = pd.DataFrame(typemap)
>>> for col in list(typedf):
>>> typedf[col].value_counts()
<class 'float'> 1016
Name: id, dtype: int64
<class 'dict'> 1010
Name: se_category, dtype: int64
<class 'float'> 1010
Name: se_document_sentiment, dtype: int64
<class 'int'> 1010
Name: se_entity_hits, dtype: int64
...
它继续走得更远,但它们都是一种类型;否则他们就是非法。
我如何在火花中做到这一点?以下是一些不起作用的尝试:
>>> outputDf = rdd.toDF()
...
ValueError: Some of types cannot be determined by the first 100 rows, please try again with sampling
>>> outputDf = rdd.toDF(sampleRatio=0.1)
...
File "/usr/hdp/current/spark-client/python/pyspark/sql/types.py", line 905, in <lambda>
return lambda row: dict((kconv(k), vconv(v)) for k, v in row.items())
AttributeError: 'NoneType' object has no attribute 'items'
这是什么问题?为什么在只有一个python数据类型的列中找出数据类型如此困难?
答案 0 :(得分:0)
此处的解决方案在
行<class 'float'> 1016
Name: id, dtype: int64
<class 'dict'> 1010
Name: se_category, dtype: int64
此rdd共有1016行;但是其中6个上升了,列se_category不存在。这就是为什么你只看到1010 dict
个对象。这对于pandas来说没有问题,它只是从列的其余部分推断出类型并使用任何适当类型的空对象(list - &gt; []; dict - &gt; {}; float或int - &gt; NaN)到填补空白。
Spark不会这样做。如果从Java的角度考虑它,这是rdd对象的基础语言,这就完全有道理了。由于我一直在编写python,一种动态类型的语言,一段时间以来,我没有立即想到这是一个问题。但是在静态类型的语言中,可以预期某些东西在编译时具有已定义的类型。
解决方案是将每一行作为一组具有类型的对象“声明”返回到rdd;从而模仿静态打字。所以我宣布
{"int_field": 0; "list_field": []; "float_field": 0.0, "string_field": ""}
在我填写任何值之前。这样,如果我的函数没有更新该值,则生成rdd;该行仍然具有所有正确的类型,并且
outputDf = rdd.map(lambda x: Row(**x)).toDF()
成功将此rdd转换为数据帧。