9

Spark's StringIndexer è piuttosto utile, ma è normale che sia necessario recuperare le corrispondenze tra i valori dell'indice generato e le stringhe originali e sembra che ci debba essere un modo incorporato per eseguire Questo. Illustrerò utilizzare questo semplice esempio dal Spark documentation:Conserva l'indicizzatore di stringhe di indici stringa di corrispondenza

from pyspark.ml.feature import StringIndexer 

df = sqlContext.createDataFrame(
    [(0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c")], 
    ["id", "category"]) 
indexer = StringIndexer(inputCol="category", outputCol="categoryIndex") 
indexed_df = indexer.fit(df).transform(df) 

Questo caso semplificata ci dà:

+---+--------+-------------+ 
| id|category|categoryIndex| 
+---+--------+-------------+ 
| 0|  a|   0.0| 
| 1|  b|   2.0| 
| 2|  c|   1.0| 
| 3|  a|   0.0| 
| 4|  a|   0.0| 
| 5|  c|   1.0| 
+---+--------+-------------+ 

tutto bene e dandy, ma per molti casi di utilizzo voglio sapere la mappatura tra il mio stringhe originali e le etichette indice. Il modo più semplice che posso pensare di farlo fuori mano è qualcosa di simile:

In [8]: indexed.select('category','categoryIndex').distinct().show() 
+--------+-------------+ 
|category|categoryIndex| 
+--------+-------------+ 
|  b|   2.0| 
|  c|   1.0| 
|  a|   0.0| 
+--------+-------------+ 

Il risultato di cui ho potuto conservare come un dizionario o simili se volevo:

In [12]: mapping = {row.categoryIndex:row.category for row in 
      indexed.select('category','categoryIndex').distinct().collect()} 

In [13]: mapping 
Out[13]: {0.0: u'a', 1.0: u'c', 2.0: u'b'} 

La mia domanda è questa : Dato che questo è un compito molto comune, e sto indovinando (ma ovviamente potrei sbagliarmi) che l'indicizzatore di stringhe è in qualche modo in grado di memorizzare questa mappatura in ogni caso, c'è un modo per svolgere il compito sopra più semplicemente?

La mia soluzione è più o meno semplice, ma per le strutture di dati di grandi dimensioni questo comporta un sacco di calcoli extra che (forse) posso evitare. Idee?

risposta

6

mappatura etichette può estratta dalla colonna metadati:

meta = [ 
    f.metadata for f in indexed_df.schema.fields if f.name == "categoryIndex" 
] 
meta[0] 
## {'ml_attr': {'name': 'category', 'type': 'nominal', 'vals': ['a', 'c', 'b']}} 

dove ml_attr.vals fornire una mappatura tra la posizione e l'etichetta:

dict(enumerate(meta[0]["ml_attr"]["vals"])) 
## {0: 'a', 1: 'c', 2: 'b'} 

Spark 1.6 +

È possibile convertire numerico valori alle etichette usando IndexToString. Questo utilizzerà i metadati della colonna come mostrato sopra.

from pyspark.ml.feature import IndexToString 

idx_to_string = IndexToString(
    inputCol="categoryIndex", outputCol="categoryValue") 

idx_to_string.transform(indexed_df).drop("id").distinct().show() 
## +--------+-------------+-------------+ 
## |category|categoryIndex|categoryValue| 
## +--------+-------------+-------------+ 
## |  b|   2.0|   b| 
## |  a|   0.0|   a| 
## |  c|   1.0|   c| 
## +--------+-------------+-------------+ 

Spark < = 1,5

Si tratta di un hack sporco, ma si può semplicemente estrarre le etichette da un indicizzatore Java come segue:

from pyspark.ml.feature import StringIndexerModel 

# A simple monkey patch so we don't have to _call_java later 
def labels(self): 
    return self._call_java("labels") 

StringIndexerModel.labels = labels 

# Fit indexer model 
indexer = StringIndexer(inputCol="category", outputCol="categoryIndex").fit(df) 

# Extract mapping 
mapping = dict(enumerate(indexer.labels())) 
mapping 
## {0: 'a', 1: 'c', 2: 'b'} 
Problemi correlati