代码之家  ›  专栏  ›  技术社区  ›  kingledion

如何推断pyspark数据帧的模式?

  •  1
  • kingledion  · 技术社区  · 6 年前

    关于如何将pyspark RDD转换为数据帧,该站点上有许多问题。但它们都没有回答如何在保留类型的同时将SQL表样式的RDD转换为数据帧的问题。

    我有一个RDD,它正好是Python中的dict列表:

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

    所有行都有相同的列。所有列的数据类型都相同。这在熊猫身上变成一个数据框架是很简单的。

    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数据类型的列中很难找到数据类型?

    1 回复  |  直到 6 年前
        1
  •  0
  •   kingledion    6 年前

    这里的解决方案是可行的

    <class 'float'>    1016
    Name: id, dtype: int64
    <class 'dict'>    1010
    Name: se_category, dtype: int64
    

    在这个RDD中总共有1016行;但是在其中6行中,没有列SE_类别。所以你只能看到1010 dict 物体。这对pandas来说没有问题,它只是从列的其余部分推断类型,并使用适当类型的空对象(list->[]dict->float或int->nan)填充空白。

    火花不会那样做。如果你从Java的角度来考虑它,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转换为数据帧。