Spark機器學習之特徵提取、選擇、轉換

本節介紹了處理特徵的算法,大致分爲以下幾組:
     1、提取:從“原始”數據提取特徵
     2、轉換:縮放,轉換或修改要素
     3、選擇:從一組較大的要素中選擇一個子集
     4、局部敏感哈希(LSH):這類算法將特徵變換的方面與其他算法相結合。


1、特徵提取
1.1 TF-IDF
(term frequency–inverse document frequency/詞頻-逆文本/文檔頻率)
    詞頻-逆文本頻率(TF-IDF)是在文本挖掘中廣泛使用的特徵向量化方法,反映了語料庫中一個詞對文檔的重要性(語料庫中的其中一份文檔的重要程度)。 
用t表示一個單詞,用d表示文檔,用D表示語料庫。詞頻率TF(t,d)是單詞t出現在文檔d中的次數,而文檔頻率DF(t,D)是單詞t在語料庫D中的頻率(出現單詞t的文檔的次數)。 
如果我們只使用詞頻率來測量重要性,那麼很容易過分強調出現得非常頻繁但是攜帶關於文檔的信息很少的單詞,例如, “a”,“the”和“of”。 如果一個單詞在語料庫中經常出現,
則意味着它不攜帶關於特定文檔的特殊信息。 逆文本頻率是一個單詞提供多少信息的數值度量:

 
                            
    其中| D | 是語料庫中文檔的總數。 由於使用對數,如果一個單詞出現在所有文檔中,則其IDF值變爲0。如果一個詞越常見,那麼分母就越大,逆文檔頻率就越小越接近0。
分母之所以要加1,是爲了避免分母爲0(即所有文檔都不包含該單詞的情況下出現0)。TF-IDF度量僅僅是TF和IDF的乘積:
                                                                                                             
    詞頻率和文檔頻率的定義有幾個變體。 在MLlib中,我們分離TF和IDF以使它們靈活。
    
    TF:HashingTF和CountVectorizer都可以用於生成詞頻率向量。
    HashingTF是一個變換器,它採用詞集合並將這些集合轉換成固定長度的特徵向量。在文本處理中,“一組詞”可能是一袋詞。 HashingTF使用散列技巧。
通過應用散列函數將原始要素映射到索引。這裏使用的散列函數是MurmurHash 3。然後基於映射的索引來計算項頻率。這種方法避免了計算全局術語到索引映射的需要,
其對於大語料庫可能是昂貴的,但是它遭受潛在的哈希衝突,其中不同的原始特徵可能在哈希後變成相同的術語。爲了減少衝突的機會,我們可以增加目標要素維度,即哈希表的桶數。
由於使用簡單模數將散列函數轉換爲列索引,因此建議使用2的冪作爲要素維度,否則不會將要素均勻映射到列。默認要素尺寸爲2的18次方 = 262144。可選的二進制切換參數控制單詞頻率計數。
設置爲true時,所有非零頻率計數都設置爲1.這對於模擬二進制計數而不是整數計數的離散概率模型特別有用。
    CountVectorizer將文本文檔轉換爲單詞計數的向量。 有關更多詳細信息,請參閱CountVectorizer。
    IDF:IDF是一個擬合數據集並生成IDFModel的估計器。 IDFModel獲取特徵向量(通常由HashingTF或CountVectorizer創建)並縮放每個列。 直觀地,它降低了語料庫中頻繁出現的列的權重。
    注意:spark.ml不提供文本分段工具。 推薦用戶使用NLP、scalanlp和chalk。
    
    Examples:
    在下面的代碼段中,我們從一組句子開始。 我們使用Tokenizer將每個句子分成單詞。 對於每個句子(詞袋),我們使用HashingTF將句子散列成特徵向量。 我們使用IDF重新縮放特徵向量; 
這通常會提高使用文本作爲特徵時的性能。 然後我們的特徵向量可以傳遞到學習算法中。


from pyspark.ml.feature import HashingTF, IDF, Tokenizer
sentenceData = spark.createDataFrame([
    (0.0, "Hi I heard about Spark"),
    (0.0, "I wish Java could use case classes"),
    (1.0, "Logistic regression models are neat")
    ],["label", "sentence"])


tokenizer = Tokenizer(inputCol="sentence", outputCol="words")
wordsData = tokenizer.transform(sentenceData)


hashingTF = HashingTF(inputCol="words", outputCol="rawFeatures", numFeatures=20)
featurizedData = hashingTF.transform(wordsData)
#CountVectorizer也可以用於獲得項頻率向量


idf = IDF(inputCol="rawFeatures", outputCol="features")
idfModel = idf.fit(featurizedData)
rescaledData = idfModel.transform(featurizedData)


rescaledData.select("label", "features").show()
rescaledData.take(1)
輸出:
[Row(label=0.0, sentence='Hi I heard about Spark', words=['hi', 'i', 'heard', 'about', 'spark'],
rawFeatures=SparseVector(20, {0: 1.0, 5: 1.0, 9: 1.0, 17: 2.0}), features=SparseVector(20, {0: 0.6931, 5: 0.6931, 9: 0.2877, 17: 1.3863}))]

1.2 Word2Vec
Word2Vec是一個估計器,它接受代表文檔的單詞序列,並訓練一個Word2VecModel。 該模型將每個詞映射到唯一的固定大小的向量。 Word2VecModel使用文檔中所有單詞的平均值將每個文檔轉換爲向量; 此向量可以用作預測,文檔相似性計算等的特徵。有關詳細信息,請參閱ML2ib用戶指南。

在下面的代碼段中,我們從一組文檔開始,每個文檔表示爲一個單詞序列。 對於每個文檔,我們將其轉換爲特徵向量。 然後可以將該特徵向量傳遞到學習算法

from pyspark.ml.feature import Word2Vec

# Input data: Each row is a bag of words from a sentence or document.
documentDF = spark.createDataFrame([
    ("Hi I heard about Spark".split(" "), ),
    ("I wish Java could use case classes".split(" "), ),
    ("Logistic regression models are neat".split(" "), )
], ["text"])

# Learn a mapping from words to Vectors.
word2Vec = Word2Vec(vectorSize=3, minCount=0, inputCol="text", outputCol="result")
model = word2Vec.fit(documentDF)

result = model.transform(documentDF)
for row in result.collect():
    text, vector = row
    print("Text: [%s] => \nVector: %s\n" % (", ".join(text), str(vector)))
1.3 CountVectorizer

CountVectorizer和CountVectorizerModel旨在幫助將文本文檔的集合轉換爲令牌計數的向量。 當一個先驗字典不可用時,CountVectorizer可以用作估計器來提取詞彙表,並生成CountVectorizerModel。 該模型爲詞彙表上的文檔生成稀疏表示,然後可以將其傳遞給其他算法,如LDA。

在擬合過程中,CountVectorizer將選擇通過語料庫的詞頻率排序的頂部vocabSize詞。 可選參數minDF還通過指定詞彙必須出現在詞彙表中的文檔的最小數量(或小於1.0)來影響擬合過程。 另一個可選的二進制切換參數控制輸出向量。 如果設置爲true,則所有非零計數都設置爲1.這對於模擬二進制計數而不是整數計數的離散概率模型特別有用。

Examples:

假設我們有以下DataFrame和列id和文本:

 id | texts
----|----------
 0  | Array("a", "b", "c")
 1  | Array("a", "b", "b", "c", "a")
文本中的每一行都是Array [String]類型的文檔。 調用CountVectorizer的匹配產生具有詞彙(a,b,c)的CountVectorizerModel。 然後轉換後的輸出列“向量”包含

id | texts                           | vector
----|---------------------------------|---------------
 0  | Array("a", "b", "c")            | (3,[0,1,2],[1.0,1.0,1.0])
 1  | Array("a", "b", "b", "c", "a")  | (3,[0,1,2],[2.0,2.0,1.0])
from pyspark.ml.feature import CountVectorizer

# Input data: Each row is a bag of words with a ID.
df = spark.createDataFrame([
    (0, "a b c".split(" ")),
    (1, "a b b c a".split(" "))
], ["id", "words"])

# fit a CountVectorizerModel from the corpus.
cv = CountVectorizer(inputCol="words", outputCol="features", vocabSize=3, minDF=2.0)

model = cv.fit(df)

result = model.transform(df)
result.show(truncate=False)
2 特徵變換

2.1 Tokenizer /分詞器

標記化是獲取文本(例如句子)並將其分解成單個術語(通常是單詞)的過程。 一個簡單的Tokenizer類提供了這個功能。 下面的例子顯示瞭如何將句子分成單詞序列。

RegexTokenizer允許基於正則表達式(regex)匹配的更高級的標記化。 默認情況下,參數“pattern”(regex,默認:“\\ s +”)用作分隔符以分隔輸入文本。 或者,用戶可以將參數“gap”設置爲false,指示正則表達式“模式”表示“令牌”,而不是分割間隙,並且找到所有匹配的出現作爲令牌化結果。

from pyspark.ml.feature import Tokenizer, RegexTokenizer
from pyspark.sql.functions import col, udf
from pyspark.sql.types import IntegerType

sentenceDataFrame = spark.createDataFrame([
    (0, "Hi I heard about Spark"),
    (1, "I wish Java could use case classes"),
    (2, "Logistic,regression,models,are,neat")
], ["id", "sentence"])

tokenizer = Tokenizer(inputCol="sentence", outputCol="words")

regexTokenizer = RegexTokenizer(inputCol="sentence", outputCol="words", pattern="\\W")
# alternatively, pattern="\\w+", gaps(False)

countTokens = udf(lambda words: len(words), IntegerType())

tokenized = tokenizer.transform(sentenceDataFrame)
tokenized.select("sentence", "words")\
    .withColumn("tokens", countTokens(col("words"))).show(truncate=False)

regexTokenized = regexTokenizer.transform(sentenceDataFrame)
regexTokenized.select("sentence", "words") \
    .withColumn("tokens", countTokens(col("words"))).show(truncate=False)
2.2 StopWordsRemover 

停止詞是應該從輸入中排除的詞,通常是因爲詞頻繁出現並且不具有任何意義。
StopWordsRemover接受字符串序列(例如Tokenizer的輸出)作爲輸入,並從輸入序列中刪除所有停止詞。 停用詞列表由stopWords參數指定。 某些語言的默認停用詞可通過調用StopWordsRemover.loadDefaultStopWords(語言)訪問,其中可用的選項是“danish”,“dutch”,“english”,“finnish”,“french”,“german”,“hungarian” “意大利語”,“挪威語”,“葡萄牙語”,“俄語”,“西班牙語”,“瑞典語”和“土耳其語”。 布爾參數caseSensitive指示匹配是否區分大小寫(默認爲false)。

例子:
假設我們有以下DataFrame和列id和raw:

id | raw
----|----------
 0  | [I, saw, the, red, baloon]
 1  | [Mary, had, a, little, lamb]
應用StopScriptRemover,將raw作爲輸入列,並過濾爲輸出列,我們應該得到以下結果:

id | raw                         | filtered
----|-----------------------------|--------------------
 0  | [I, saw, the, red, baloon]  |  [saw, red, baloon]
 1  | [Mary, had, a, little, lamb]|[Mary, little, lamb]
在過濾時,停止詞“I”,“the”,“had”和“a”已被過濾掉。

from pyspark.ml.feature import StopWordsRemover

sentenceData = spark.createDataFrame([
    (0, ["I", "saw", "the", "red", "balloon"]),
    (1, ["Mary", "had", "a", "little", "lamb"])
], ["id", "raw"])

remover = StopWordsRemover(inputCol="raw", outputCol="filtered")
remover.transform(sentenceData).show(truncate=False)
2.3 n-gram

n-gram是對於某個整數n的n個令牌(通常爲字)的序列。 NGram類可以用於將輸入特徵轉換爲n-gram。
NGram將字符串序列(例如,Tokenizer的輸出)作爲輸入。 參數n用於確定每個n-gram中的項的數量。 輸出將包括一個n-gram序列,其中每個n-gram由n個連續字的空格分隔的字符串表示。 如果輸入序列包含少於n個字符串,則不會生成輸出。

from pyspark.ml.feature import NGram

wordDataFrame = spark.createDataFrame([
    (0, ["Hi", "I", "heard", "about", "Spark"]),
    (1, ["I", "wish", "Java", "could", "use", "case", "classes"]),
    (2, ["Logistic", "regression", "models", "are", "neat"])
], ["id", "words"])

ngram = NGram(n=2, inputCol="words", outputCol="ngrams")

ngramDataFrame = ngram.transform(wordDataFrame)
ngramDataFrame.select("ngrams").show(truncate=False)

2.4 Binarizer (二值化

二值化是將數字特徵閾值爲二進制(0/1)特徵的過程。
Binarizer接受通用參數inputCol和outputCol以及二進制閾值。 大於閾值的特徵值被二進制化爲1.0; 等於或小於閾值的值被二值化爲0.0。 inputCol支持Vector和Double類型。

from pyspark.ml.feature import Binarizer

continuousDataFrame = spark.createDataFrame([
    (0, 0.1),
    (1, 0.8),
    (2, 0.2)
], ["id", "feature"])

binarizer = Binarizer(threshold=0.5, inputCol="feature", outputCol="binarized_feature")

binarizedDataFrame = binarizer.transform(continuousDataFrame)

print("Binarizer output with Threshold = %f" % binarizer.getThreshold())
binarizedDataFrame.show()
2.5 PCA(主成分分析

PCA是使用正交變換將可能相關的變量的觀察值的集合轉換成稱爲主分量的線性不相關變量的值的集合的統計過程。 PCA類訓練模型使用PCA將向量投影到低維空間。 下面的示例顯示瞭如何將5維特徵向量投影到3維主成分中。

from pyspark.ml.feature import PCA
from pyspark.ml.linalg import Vectors

data = [(Vectors.sparse(5, [(1, 1.0), (3, 7.0)]),),
        (Vectors.dense([2.0, 0.0, 3.0, 4.0, 5.0]),),
        (Vectors.dense([4.0, 0.0, 0.0, 6.0, 7.0]),)]
df = spark.createDataFrame(data, ["features"])

pca = PCA(k=3, inputCol="features", outputCol="pcaFeatures")
model = pca.fit(df)

result = model.transform(df).select("pcaFeatures")
result.show(truncate=False)
2.6 PolynomialExpansion (多項式擴展

多項式展開是將特徵擴展到多項式空間的過程,該多項式空間由原始尺寸的n度組合表示。 PolynomialExpansion類提供此功能。 下面的示例顯示瞭如何將您的要素擴展到3度多項式空間。

from pyspark.ml.feature import PolynomialExpansion
from pyspark.ml.linalg import Vectors

df = spark.createDataFrame([
    (Vectors.dense([2.0, 1.0]),),
    (Vectors.dense([0.0, 0.0]),),
    (Vectors.dense([3.0, -1.0]),)
], ["features"])

polyExpansion = PolynomialExpansion(degree=3, inputCol="features", outputCol="polyFeatures")
polyDF = polyExpansion.transform(df)

polyDF.show(truncate=False)
2.7 Discrete Cosine Transform (DCT-離散餘弦變換)

離散餘弦變換將時域中的長度N實值序列變換爲頻域中的另一長度N實值序列。 DCT類提供此功能,實現DCT-II並且通過1/√2/縮放結果,使得用於變換的表示矩陣是統一的。 沒有偏移被應用於變換序列(例如,變換序列的第0個元素是第0個DCT係數而不是第N / 2個)。

from pyspark.ml.feature import DCT
from pyspark.ml.linalg import Vectors

df = spark.createDataFrame([
    (Vectors.dense([0.0, 1.0, -2.0, 3.0]),),
    (Vectors.dense([-1.0, 2.0, 4.0, -7.0]),),
    (Vectors.dense([14.0, -2.0, -5.0, 1.0]),)], ["features"])

dct = DCT(inverse=False, inputCol="features", outputCol="featuresDCT")

dctDf = dct.transform(df)

dctDf.select("featuresDCT").show(truncate=False)
2.8 StringIndexer 字符串索引

StringIndexer將標籤的字符串列編碼爲標籤索引列。 索引位於[0,numLabels),按標籤頻率排序,因此最常見的標籤獲取索引0.如果輸入列是數字,我們將其轉換爲字符串並索引字符串值。 當下遊管道組件(如Estimator或Transformer)使用此字符串索引標籤時,必須將組件的輸入列設置爲此字符串索引的列名稱。 在許多情況下,可以使用setInputCol設置輸入列。

例子
假設我們有以下DataFrame和列id和category:

id | category
----|----------
 0  | a
 1  | b
 2  | c
 3  | a
 4  | a
 5  | c
category是具有三個標籤:“a”,“b”和“c”的字符串列。 應用StringIndexer,其中category作爲輸入列,categoryIndex作爲輸出列,我們應該得到以下結果:

 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
“a”獲得索引0,因爲它是最頻繁的,隨後是具有索引1的“c”和具有索引2的“b”。
此外,有兩個策略,關於StringIndexer如何處理未見的標籤,當你已經適應一個StringIndexer在一個數據集,然後使用它來轉換另一個:
拋出異常(這是默認值)
請跳過包含未看見標籤的行

例子
讓我們回到前面的例子,但這次重用我們以前定義的StringIndexer在下面的數據集:

id | category
----|----------
 0  | a
 1  | b
 2  | c
 3  | d
如果您沒有設置StringIndexer如何處理未看見的標籤或將其設置爲“錯誤”,則會拋出異常。 但是,如果您調用了setHandleInvalid(“skip”),將生成以下數據集:

 id | category | categoryIndex
----|----------|---------------
 0  | a        | 0.0
 1  | b        | 2.0
 2  | c        | 1.0
請注意,包含“d”的行不會出現。

from pyspark.ml.feature import StringIndexer

df = spark.createDataFrame(
    [(0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c")],
    ["id", "category"])

indexer = StringIndexer(inputCol="category", outputCol="categoryIndex")
indexed = indexer.fit(df).transform(df)
indexed.show()
2.9 IndexToString

對稱到StringIndexer,IndexToString將一列標籤索引映射回包含原始標籤的列作爲字符串。 一個常見的用例是使用StringIndexer從標籤生成索引,使用這些索引訓練模型,並從IndexToString的預測索引列中檢索原始標籤。 但是,您可以自由提供您自己的標籤。
例子
基於StringIndexer示例,讓我們假設我們有以下DataFrame和列id和categoryIndex:

 id | categoryIndex
----|---------------
 0  | 0.0
 1  | 2.0
 2  | 1.0
 3  | 0.0
 4  | 0.0
 5  | 1.0
應用IndexToString,將categoryIndex作爲輸入列,將originalCategory作爲輸出列,我們可以檢索我們的原始標籤(它們將從列的元數據中推斷):

id | categoryIndex | originalCategory
----|---------------|-----------------
 0  | 0.0           | a
 1  | 2.0           | b
 2  | 1.0           | c
 3  | 0.0           | a
 4  | 0.0           | a
 5  | 1.0           | c
from pyspark.ml.feature import IndexToString, StringIndexer

df = spark.createDataFrame(
    [(0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c")],
    ["id", "category"])

indexer = StringIndexer(inputCol="category", outputCol="categoryIndex")
model = indexer.fit(df)
indexed = model.transform(df)

print("Transformed string column '%s' to indexed column '%s'"
      % (indexer.getInputCol(), indexer.getOutputCol()))
indexed.show()

print("StringIndexer will store labels in output column metadata\n")

converter = IndexToString(inputCol="categoryIndex", outputCol="originalCategory")
converted = converter.transform(indexed)

print("Transformed indexed column '%s' back to original string column '%s' using "
      "labels in metadata" % (converter.getInputCol(), converter.getOutputCol()))
converted.select("id", "categoryIndex", "originalCategory").show()
2.10 OneHotEncoder(獨熱碼)

獨熱編碼將一列標籤索引映射到二進制向量列,最多具有單個一值。 此編碼允許期望連續特徵(例如邏輯迴歸)的算法使用分類特徵。

from pyspark.ml.feature import OneHotEncoder, StringIndexer

df = spark.createDataFrame([
    (0, "a"),
    (1, "b"),
    (2, "c"),
    (3, "a"),
    (4, "a"),
    (5, "c")
], ["id", "category"])

stringIndexer = StringIndexer(inputCol="category", outputCol="categoryIndex")
model = stringIndexer.fit(df)
indexed = model.transform(df)

encoder = OneHotEncoder(inputCol="categoryIndex", outputCol="categoryVec")
encoded = encoder.transform(indexed)
encoded.show()
2.11 VectorIndexer
VectorIndexer幫助索引向量數據集中的分類特徵。它可以自動決定哪些特徵是分類的,並將原始值轉換爲類別索引。具體來說,它執行以下操作:
1、獲取Vector類型的輸入列和參數maxCategories。
2、基於不同值的數量確定哪些特徵應是分類的,其中最多maxCategories的特徵被聲明爲分類。
3、爲每個分類特徵計算基於0的類別索引。
4、索引分類特徵並將原始特徵值轉換爲索引。
5、索引分類特徵允許諸如決策樹和樹集合等算法適當地處理分類特徵,從而提高性能。
在下面的例子中,我們讀取一個標記點​​的數據集,然後使用VectorIndexer來決定哪些特徵應該被視爲分類。我們將分類特徵值轉換爲它們的索引。這個變換的數據然後可以被傳遞到諸如DecisionTreeRegressor的處理分類特徵的算法。

from pyspark.ml.feature import VectorIndexer

data = spark.read.format("libsvm").load("data/mllib/sample_libsvm_data.txt")

indexer = VectorIndexer(inputCol="features", outputCol="indexed", maxCategories=10)
indexerModel = indexer.fit(data)

categoricalFeatures = indexerModel.categoryMaps
print("Chose %d categorical features: %s" %
      (len(categoricalFeatures), ", ".join(str(k) for k in categoricalFeatures.keys())))

# Create new column "indexed" with categorical values transformed to indices
indexedData = indexerModel.transform(data)
indexedData.show()
2.12 Interaction

交互是一個變換器,它使用向量或雙值列,並生成單個向量列,其中包含每個輸入列的一個值的所有組合的乘積。例如,如果您有兩個向量類型列,每個列有3個維度作爲輸入列,那麼您將獲得一個9維向量作爲輸出列。
例子
假設我們有以下DataFrame的列“id1”,“vec1”和“vec2”:

id1|vec1          |vec2          
  ---|--------------|--------------
  1  |[1.0,2.0,3.0] |[8.0,4.0,5.0] 
  2  |[4.0,3.0,8.0] |[7.0,9.0,8.0] 
  3  |[6.0,1.0,9.0] |[2.0,3.0,6.0] 
  4  |[10.0,8.0,6.0]|[9.0,4.0,5.0] 
  5  |[9.0,2.0,7.0] |[10.0,7.0,3.0]
  6  |[1.0,1.0,4.0] |[2.0,8.0,4.0]   
應用與這些輸入列的交互,然後interactionedCol作爲輸出列包含:

id1|vec1          |vec2          |interactedCol                                         
  ---|--------------|--------------|------------------------------------------------------
  1  |[1.0,2.0,3.0] |[8.0,4.0,5.0] |[8.0,4.0,5.0,16.0,8.0,10.0,24.0,12.0,15.0]            
  2  |[4.0,3.0,8.0] |[7.0,9.0,8.0] |[56.0,72.0,64.0,42.0,54.0,48.0,112.0,144.0,128.0]     
  3  |[6.0,1.0,9.0] |[2.0,3.0,6.0] |[36.0,54.0,108.0,6.0,9.0,18.0,54.0,81.0,162.0]        
  4  |[10.0,8.0,6.0]|[9.0,4.0,5.0] |[360.0,160.0,200.0,288.0,128.0,160.0,216.0,96.0,120.0]
  5  |[9.0,2.0,7.0] |[10.0,7.0,3.0]|[450.0,315.0,135.0,100.0,70.0,30.0,350.0,245.0,105.0] 
  6  |[1.0,1.0,4.0] |[2.0,8.0,4.0] |[12.0,48.0,24.0,12.0,48.0,24.0,48.0,192.0,96.0]    
import org.apache.spark.ml.feature.Interaction
import org.apache.spark.ml.feature.VectorAssembler

val df = spark.createDataFrame(Seq(
  (1, 1, 2, 3, 8, 4, 5),
  (2, 4, 3, 8, 7, 9, 8),
  (3, 6, 1, 9, 2, 3, 6),
  (4, 10, 8, 6, 9, 4, 5),
  (5, 9, 2, 7, 10, 7, 3),
  (6, 1, 1, 4, 2, 8, 4)
)).toDF("id1", "id2", "id3", "id4", "id5", "id6", "id7")

val assembler1 = new VectorAssembler().
  setInputCols(Array("id2", "id3", "id4")).
  setOutputCol("vec1")

val assembled1 = assembler1.transform(df)

val assembler2 = new VectorAssembler().
  setInputCols(Array("id5", "id6", "id7")).
  setOutputCol("vec2")

val assembled2 = assembler2.transform(assembled1).select("id1", "vec1", "vec2")

val interaction = new Interaction()
  .setInputCols(Array("id1", "vec1", "vec2"))
  .setOutputCol("interactedCol")

val interacted = interaction.transform(assembled2)

interacted.show(truncate = false)
2.13 Normalizer (正則化

Normalizer是一個變換器,用於變換向量行的數據集,規範每個向量以具有單位範數。 它採用參數p,其指定用於歸一化的p範數。 (默認情況下p = 2)。這種歸一化可以幫助標準化輸入數據並改進學習算法的行爲。
以下示例演示如何加載libsvm格式的數據集,然後將每行標準化爲具有單位L1範數和單位L∞範數。

from pyspark.ml.feature import Normalizer
from pyspark.ml.linalg import Vectors

dataFrame = spark.createDataFrame([
    (0, Vectors.dense([1.0, 0.5, -1.0]),),
    (1, Vectors.dense([2.0, 1.0, 1.0]),),
    (2, Vectors.dense([4.0, 10.0, 2.0]),)
], ["id", "features"])

# Normalize each Vector using $L^1$ norm.
normalizer = Normalizer(inputCol="features", outputCol="normFeatures", p=1.0)
l1NormData = normalizer.transform(dataFrame)
print("Normalized using L^1 norm")
l1NormData.show()

# Normalize each Vector using $L^\infty$ norm.
lInfNormData = normalizer.transform(dataFrame, {normalizer.p: float("inf")})
print("Normalized using L^inf norm")
lInfNormData.show()
2.14 StandardScaler(標準化縮放)

StandardScaler變換向量行的數據集,規範每個特徵以具有單位標準偏差和/或零均值。 它需要參數:
withStd:默認爲True。 將數據縮放到單位標準偏差。
withMean:默認爲False。 在縮放之前用平均值居中數據。 它將構建密集輸出,因此在應用於稀疏輸入時要小心。
StandardScaler是一個估計器,可以適合數據集以產生一個StandardScalerModel; 這相當於計算摘要統計。 然後,模型可以將數據集中的向量列變換爲具有單位標準偏差和/或零均值特徵。
請注意,如果要素的標準偏差爲零,則該要素的向量中將返回默認值0.0。
以下示例演示如何以libsvm格式加載數據集,然後規範化每個要素的單位標準偏差。

from pyspark.ml.feature import StandardScaler

dataFrame = spark.read.format("libsvm").load("data/mllib/sample_libsvm_data.txt")
scaler = StandardScaler(inputCol="features", outputCol="scaledFeatures",
                        withStd=True, withMean=False)

# Compute summary statistics by fitting the StandardScaler
scalerModel = scaler.fit(dataFrame)

# Normalize each feature to have unit standard deviation.
scaledData = scalerModel.transform(dataFrame)
scaledData.show()
2.15 MinMaxScaler

MinMaxScaler變換矢量行的數據集,將每個要素重新縮放到特定範圍(通常爲[0,1])。 它需要參數:
min:默認值爲0.0。 轉換後的下邊界,由所有特徵共享。
max:1.0。 轉換後的上界,由所有特徵共享。
MinMaxScaler計算數據集的彙總統計信息,並生成MinMaxScalerModel。 然後,模型可以單獨變換每個特徵,使得它在給定範圍內。
特徵E的重新縮放的值被計算爲,


對於
請注意,由於零值可能會轉換爲非零值,即使對於稀疏輸入,變壓器的輸出也將是DenseVector。
以下示例演示如何以libsvm格式加載數據集,然後將每個要素重新縮放爲[0,1]。

from pyspark.ml.feature import MinMaxScaler
from pyspark.ml.linalg import Vectors

dataFrame = spark.createDataFrame([
    (0, Vectors.dense([1.0, 0.1, -1.0]),),
    (1, Vectors.dense([2.0, 1.1, 1.0]),),
    (2, Vectors.dense([3.0, 10.1, 3.0]),)
], ["id", "features"])

scaler = MinMaxScaler(inputCol="features", outputCol="scaledFeatures")

# Compute summary statistics and generate MinMaxScalerModel
scalerModel = scaler.fit(dataFrame)

# rescale each feature to range [min, max].
scaledData = scalerModel.transform(dataFrame)
print("Features scaled to range: [%f, %f]" % (scaler.getMin(), scaler.getMax()))
scaledData.select("features", "scaledFeatures").show()

2.16 MaxAbsScaler

MaxAbsScaler轉換Vector行的數據集,通過劃分每個要素中的最大絕對值,將每個要素重新縮放到range [-1,1]。 它不會移動/居中數據,因此不會破壞任何稀疏性。MaxAbsScaler計算數據集上的彙總統計信息,並生成MaxAbsScalerModel。 然後,模型可以將每個要素單獨轉換爲range [-1,1]。以下示例演示如何加載libsvm格式的數據集,然後將每個要素重新縮放到[-1,1]。

from pyspark.ml.feature import MaxAbsScaler
from pyspark.ml.linalg import Vectors

dataFrame = spark.createDataFrame([
    (0, Vectors.dense([1.0, 0.1, -8.0]),),
    (1, Vectors.dense([2.0, 1.0, -4.0]),),
    (2, Vectors.dense([4.0, 10.0, 8.0]),)
], ["id", "features"])

scaler = MaxAbsScaler(inputCol="features", outputCol="scaledFeatures")

# Compute summary statistics and generate MaxAbsScalerModel
scalerModel = scaler.fit(dataFrame)

# rescale each feature to range [-1, 1].
scaledData = scalerModel.transform(dataFrame)

scaledData.select("features", "scaledFeatures").show()

2.17 Bucketizer(連續數據離散化到指定的範圍區間)

Bucketizer將一列連續要素轉換爲一列要素桶,其中的桶由用戶指定。它需要一個參數:
split:用於將連續要素映射到bucket的參數。對於n + 1分割,有n個桶。由分割x,y定義的桶保存除了最後一個桶之外的範圍[x,y)中的值,其還包括y。分割應嚴格增加。必須明確提供-inf,inf的值以涵蓋所有Double值;否則,指定拆分之外的值將被視爲錯誤。分割的兩個示例是Array(Double.NegativeInfinity,0.0,1.0,Double.PositiveInfinity)和Array(0.0,1.0,2.0)。
注意,如果你不知道目標列的上限和下限,你應該添加Double.NegativeInfinity和Double.PositiveInfinity作爲分割的邊界,以防止潛在的Bucketizer邊界異常。還要注意,您提供的分割必須嚴格按升序,即s0 <s1 <s2 <... <sn。
有關詳細信息,請參閱Bucketizer的API文檔。
以下示例演示如何將雙列列存儲到另一個索引列的列中。

from pyspark.ml.feature import Bucketizer

splits = [-float("inf"), -0.5, 0.0, 0.5, float("inf")]

data = [(-999.9,), (-0.5,), (-0.3,), (0.0,), (0.2,), (999.9,)]
dataFrame = spark.createDataFrame(data, ["features"])

bucketizer = Bucketizer(splits=splits, inputCol="features", outputCol="bucketedFeatures")

# Transform original data into its bucket index.
bucketedData = bucketizer.transform(dataFrame)

print("Bucketizer output with %d buckets" % (len(bucketizer.getSplits())-1))
bucketedData.show()

2.18 ElementwiseProduct

ElementwiseProduct使用元素級乘法將每個輸入向量乘以提供的“權重”向量。 換句話說,它通過標量乘法器來縮放數據集的每一列。 這表示輸入向量v和變換向量w之間的Hadamard乘積,以產生結果向量。


下面的示例演示瞭如何使用變換向量值來變換向量。

from pyspark.ml.feature import ElementwiseProduct
from pyspark.ml.linalg import Vectors

# Create some vector data; also works for sparse vectors
data = [(Vectors.dense([1.0, 2.0, 3.0]),), (Vectors.dense([4.0, 5.0, 6.0]),)]
df = spark.createDataFrame(data, ["vector"])
transformer = ElementwiseProduct(scalingVec=Vectors.dense([0.0, 1.0, 2.0]),
                                 inputCol="vector", outputCol="transformedVector")
# Batch transform the vectors to create new column:
transformer.transform(df).SQLTransformershow()
2.19 SQLTransformer

SQLTransformer實現由SQL語句定義的變換。 目前我們只支持SQL語法,如“SELECT ... FROM __THIS__ ...”,其中“__THIS__”表示輸入數據集的基礎表。 select子句指定要在輸出中顯示的字段,常量和表達式,並且可以是Spark SQL支持的任何select子句。 用戶還可以使用Spark SQL內置函數和UDF操作這些選定的列。 例如,SQLTransformer支持以下語句:

  • SELECT a, a + b AS a_b FROM __THIS__
  • SELECT a, SQRT(b) AS b_sqrt FROM __THIS__ where a > 5
  • SELECT a, b, SUM(c) AS c_sum FROM __THIS__ GROUP BY a, b
例子
假設我們有以下DataFrame和列id,v1和v2:

id |  v1 |  v2
----|-----|-----
 0  | 1.0 | 3.0  
 2  | 2.0 | 5.0
這是SQLTransformer "SELECT *, (v1 + v2) AS v3, (v1 * v2) AS v4 FROM __THIS__":語句的輸出

id |  v1 |  v2 |  v3 |  v4
----|-----|-----|-----|-----
 0  | 1.0 | 3.0 | 4.0 | 3.0
 2  | 2.0 | 5.0 | 7.0 |10.0
from pyspark.ml.feature import SQLTransformer

df = spark.createDataFrame([
    (0, 1.0, 3.0),
    (2, 2.0, 5.0)
], ["id", "v1", "v2"])
sqlTrans = SQLTransformer(
    statement="SELECT *, (v1 + v2) AS v3, (v1 * v2) AS v4 FROM __THIS__")
sqlTrans.transform(df).show()
2.20 VectorAssembler

VectorAssembler是一個變換器,將給定的列列表組合成一個單一的向量列。 它有用於將由不同特徵變換器生成的原始特徵和特徵組合成單個特徵向量,以便訓練ML模型,如邏輯迴歸和決策樹。 VectorAssembler接受以下輸入列類型:所有數字類型,布爾類型和向量類型。 在每一行中,輸入列的值將按指定的順序連接到向量中。
例子
假設我們有一個帶有id,hour,mobile,userFeatures和clicked列的DataFrame:

id | hour | mobile | userFeatures     | clicked
----|------|--------|------------------|---------
 0  | 18   | 1.0    | [0.0, 10.0, 0.5] | 1.0
userFeatures是一個包含三個用戶特徵的向量列。 我們希望將hour,mobile和userFeatures合併成一個稱爲特徵的單一特徵向量,並使用它來預測是否被點擊。 如果我們將VectorAssembler的輸入列設置爲hour,mobile和userFeatures,並將列輸出到要素,則在轉換後,我們應該得到以下DataFrame:

id | hour | mobile | userFeatures     | clicked | features
----|------|--------|------------------|---------|-----------------------------
 0  | 18   | 1.0    | [0.0, 10.0, 0.5] | 1.0     | [18.0, 1.0, 0.0, 10.0, 0.5]
from pyspark.ml.linalg import Vectors
from pyspark.ml.feature import VectorAssembler

dataset = spark.createDataFrame(
    [(0, 18, 1.0, Vectors.dense([0.0, 10.0, 0.5]), 1.0)],
    ["id", "hour", "mobile", "userFeatures", "clicked"])

assembler = VectorAssembler(
    inputCols=["hour", "mobile", "userFeatures"],
    outputCol="features")

output = assembler.transform(dataset)
print("Assembled columns 'hour', 'mobile', 'userFeatures' to vector column 'features'")
output.select("features", "clicked").show(truncate=False)

2.21 QuantileDiscretizer


3、特徵選擇

3.1 VectorSlicer

VectorSlicer是一個處理特徵向量的變換器,並輸出一個新的原始特徵子集的特徵向量。 從向量列中提取特徵很有用。VectorSlicer接受具有指定索引的向量列,然後輸出一個新的向量列,其值通過這些索引進行選擇。 有兩種類型的索引,

1、代表向量中的索引的整數索引,setIndices()。

2、表示向量中特徵名稱的字符串索引,setNames()。 這需要向量列具有AttributeGroup,因爲要實現在Attribute的name字段匹配。

整數和字符串的規格都可以接受。 此外,您可以同時使用整數索引和字符串名稱。 必須至少選擇一個特徵。 重複的功能是不允許的,所以選擇的索引和名稱之間不能有重疊。 請注意,如果選擇了功能的名稱,則會遇到空的輸入屬性時會拋出異常。

輸出向量將首先(按照給定的順序)對所選索引的特徵進行排序,其次是所選擇的名稱(按照給定的順序)。

例子
假設我們有一個DataFrame與列userFeatures:

userFeatures
------------------
 [0.0, 10.0, 0.5]
userFeatures是一個包含三個用戶功能的向量列。 假設userFeature的第一列全部爲零,因此我們要刪除它並僅選擇最後兩列。 VectorSlicer使用setIndices(1,2)選擇最後兩個元素,然後生成一個名爲features的新向量列:

 userFeatures     | features
------------------|-----------------------------
 [0.0, 10.0, 0.5] | [10.0, 0.5]
假設我們對userFeatures具有潛在的輸入屬性,即[“f1”,“f2”,“f3”],那麼我們可以使用setNames(“f2”,“f3”)來選擇它們。

 userFeatures     | features
------------------|-----------------------------
 [0.0, 10.0, 0.5] | [10.0, 0.5]
 ["f1", "f2", "f3"] | ["f2", "f3"]

from pyspark.ml.feature import VectorSlicer
from pyspark.ml.linalg import Vectors
from pyspark.sql.types import Row

df = spark.createDataFrame([
    Row(userFeatures=Vectors.sparse(3, {0: -2.0, 1: 2.3})),
    Row(userFeatures=Vectors.dense([-2.0, 2.3, 0.0]))])

slicer = VectorSlicer(inputCol="userFeatures", outputCol="features", indices=[1])

output = slicer.transform(df)

output.select("userFeatures", "features").show()
3.2 RFormula

公式選擇由R模型公式指定的列。 目前,我們支持R運算符的有限子集,包括'〜','。',':','+'和' - '。 基本操作有:

〜單獨的目標和條件
+連字詞,“+ 0”表示刪除截取
- 刪除術語,“ - 1”表示刪除攔截
:交互(數字乘法或二值化分類值)
. 所有列除了目標

假設a和b是雙列,我們使用以下簡單的例子來說明RFormula的效果:

y〜a + b表示模型y〜w0 + w1 * a + w2 * b,其中w0是截距,w1,w2是係數。

y〜a + b + a:b - 1表示模型y〜w1 * a + w2 * b + w3 * a * b其中w1,w2,w3爲係數。

RFormula產生一個特徵向量列和一個標籤的雙列或串列。 像R在線性迴歸中使用公式時,字符串輸入列將被單編碼,數字列將被轉換爲雙精度。 如果標籤列是類型字符串,則它將首先使用StringIndexer轉換爲double。 如果DataFrame中不存在標籤列,則會從公式中指定的響應變量創建輸出標籤列。

例子

假設我們有一個具有列id,country,hour和clicked的DataFrame:

id | country | hour | clicked
---|---------|------|---------
 7 | "US"    | 18   | 1.0
 8 | "CA"    | 12   | 0.0
 9 | "NZ"    | 15   | 0.0
如果我們使用具有clicked〜country+hour的公式字符串的RFormula,這表示我們想要基於country和hour預測clicked,轉換後我們應該得到以下DataFrame:

id | country | hour | clicked | features         | label
---|---------|------|---------|------------------|-------
 7 | "US"    | 18   | 1.0     | [0.0, 0.0, 18.0] | 1.0
 8 | "CA"    | 12   | 0.0     | [0.0, 1.0, 12.0] | 0.0
 9 | "NZ"    | 15   | 0.0     | [1.0, 0.0, 15.0] | 0.0
from pyspark.ml.feature import RFormula

dataset = spark.createDataFrame(
    [(7, "US", 18, 1.0),
     (8, "CA", 12, 0.0),
     (9, "NZ", 15, 0.0)],
    ["id", "country", "hour", "clicked"])

formula = RFormula(
    formula="clicked ~ country + hour",
    featuresCol="features",
    labelCol="label")

output = formula.fit(dataset).transform(dataset)
output.select("features", "label").show()
3.3 ChiSqSelector





發佈了141 篇原創文章 · 獲贊 36 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章