在 pandas 中编码多列

Encoding multiple columns in pandas

我对编码(我不熟悉此类任务)分类变量有一些疑问,以便将它们用作逻辑回归或 SVM 等模型中的参数。我的数据集如下所示:

Text                                  Symbol    Note    Account    Age   Label 
There is a red car                      !        red      John    24   1
My bag was very expensive               ?       orange    Luke    36  0
Where are my keys?                      @        red      Red     58  1
I promise: I will never let you go!    ...       green    Aoife   28  0

在'Text'中,存储了社区用户的评论。 'Symbol' 包括用户最常用的符号。 'Note'代表等级(绿色为经验丰富,红色为新手) 'Account'为用户名。 'Label' 提供有关用户可信度的信息(如果为 0,则用户不是假的;如果为 1,则用户可能是机器人。)

我想class根据当前信息(见上列)确定新用户。我的数据集包括 1000 多行和 400 个用户。 由于要使用 classifiers 我需要对分类和文本字段进行编码,我尝试通过在 sklearn 中使用 MultiColumnLabelEncoder 来执行以下操作:

MultiColumnLabelEncoder(columns = ['Text', 'Symbol', 'Note', 'Account']).fit_transform(df)

其中 df 是我的数据框。但是,我知道 OneHotEncoder 也应该更可取。我还包含了 'Account',因为同一帐户可能会有更多评论,所以如果我 class 将一个帐户确定为假帐户,并且我收到来自同一帐户的新评论,那么可以很容易地检测到该帐户假的。

正如我提到的,目标是class根据给定的信息(符号、注释、年龄、文本)以一定的准确性确定测试集中的新元素,即寻找这些变量之间可能的相关性可以让我说一个新帐户是假的 (1) 或不是 (0)。

如您所见,问题与 class 参数有关,其中参数不仅是数值的,而且是分类的。

对于数据预处理(去除停用词和清理数据),我使用了 Python 个 NLTK 包;关于特征提取(这应该是一个关键点,因为它与下一步相关联,即使用 classifier 来预测 class - 1 或 0),我发现很难理解我的输出应该从编码中得到期望,以便能够将上述信息用作我模型中的输入(其中目标称为标签,它是一个二进制值)。 我正在使用 classifier 逻辑回归,还有 SVM。

如果用户 X(年龄 16,符号 #,注释 Wonderful,注释红色 - 新加入者),我的预期输出将是class以一定百分比被假冒。

如果有人能一步一步地向我解释将我的数据集转换为数据集的方法,我将不胜感激,我可以在逻辑回归中使用其变量以确定标签(假的或非假的)新用户。

我是根据我的一些旧代码做的,我的一些旧代码本身是基于 scikit-learn working with text. Let me also reference, Scikit-learn 6.2.3 and note that CountVectorizer 的,因为它包含你想用 OneHotEncoder 做的事情等等。来自 CountVectorizer 文档:

CountVectorizer implements both tokenization and occurrence counting in a single class:

在您提供的示例中,您总共有 95 个单词,其中包含 22 个独特的单词——假设您使用了所有可能不是您想要的单词。换句话说,像“there, is, a, my, was, I, where and which”这样的词可能无法帮助你区分一个好账户和一个假账户,但是像“尼日利亚,王子,转账,银行,阴茎,或放大”可能表示垃圾邮件。

因此,在转到其他列(如年龄、符号等)之前,您将有 22 个维度(减去任何排除的维度)的数据。这是很多无用的数据(所有这些 0 都不是你需要的)所以人们要么将它存储为稀疏矩阵 and/or 使用某种降​​维方法,如 Lasso 或 Ridge。你可能认为这正是你现在想做的,而且你走在正确的轨道上。这与你问的有点不同。你有点像,除了还有几点需要处理。

首先,我认为这很重要,您的某些字段应该值得怀疑,因为它们是用户报告的(如年龄)或 useless/redundant(如姓名)。没有孩子会在色情网站或酒厂网站上说自己 15 岁。没有哪个顽皮的老家伙说他 65 岁了想和未成年孩子聊天。甚至是您认为人们最终会发现的约会网站。人们谎报他们的年龄。名字也是如此。如果需要,您可以包括它们,但请记住古老的格言:垃圾输入,垃圾输出。

其次,Lasso 和 Ridge 回归都分配成本函数来帮助过度拟合模型。因此,基于面积和邮政编码的房价是有道理的。但是,当您了解上次完成 属性 税务评估的时间或到最近的图书馆的距离时,您可能会想“真的吗?”但这并不是你真正拥有的东西。

把这两个放在一起,在你的情况下你有文本(绝对有用)、符号(文本的派生词)、帐户和年龄(见上面的注释)、注释(可能在它们出现时有用on 和 active),并标记——你的评估。因此,在五个字段中,只有两个可能对预测评估有用。这就是说,虽然您可以使用套索或岭,但使用贝叶斯可能会更好地完成此任务。如果你愿意的话,有多个页面会显示它们在某些条件下是等价的 [example]。但是考虑贝叶斯的原因是这个例子的计算量。

符号(第四部分)我一直不愿意这么说,但根据经验,标点符号不是一个好的指标。我说厌恶的原因是你可能想出一些新颖的实现。但是很多人都尝试过,所以可能性很小。这部分与 Zipf 定律有关,该定律与单词而不是标点符号有关。然而,如果你让标点符号带有某种额外的语义,它本质上就是另一个词。请记住,我们的目标不是找到垃圾邮件中的符号,而是找到该符号是否是垃圾邮件的可靠指标并且是否足够独特

但如果您真的想添加标点符号作为某种指示符,您可能需要换个思路。例如,仅仅存在一个问号就足够了吗?或者,连续三个或更多?或者,每个{文本、电子邮件、消息、post 等}的字符百分比很高?这涉及到特征工程,这就是为什么我会说你需要仔细考虑它的部分原因。就我个人而言(通过快速浏览我的垃圾邮件文件夹),我会查看表情符号、外来字符(例如,£)以及文本效果(粗体、下划线等)。但是你有一个单独的第二个问题。对于文本内容,你有概率加载,比如一个 aggerate 测量:

print(f"{message} is flagged for consideration at {loading}%.

但在上面建议的这些选项中,您需要为该功能开发某种权重。您 可以 将符号附加到每个文本字段但在 TF-IDF 之前。但是你需要使用不同的方法。您还可以为内容分配一个权重,为基于主成分分析 and/or 混淆矩阵的工程特征分配第二个权重。

例如 - 文本 34 是已知的垃圾邮件:

N£w Skinny Pill Kills Too Much Fat? This Diet is Sweeping The Nation

贝叶斯方法分配的垃圾邮件总概率为 94%,远高于 89% 的阈值。但已知垃圾邮件的概率为 1(00%)。 6% 的增量最有可能是由于什么?我认为在这种情况下是 £.

标签也是如此。从您的火车集中,您可能有零个超过 2 年发送垃圾邮件的帐户,并且 90% 来自不到 1 周的帐户。

无论如何,关于代码和我补充.

1。绿豆数据。

这是受监督的,因此 'Label' 根据定义是关键的。

2。 Train-test拆分

你没有提到这一点,但值得注意。 sklearn.model_selection.train_test_split

3。使用 scikit-learn.

标记文本

这就是您要具体询问的地方。将语料库(文档集合)变成 bag-of-words。您说您使用的是对学术界有益的 NLTK,但我发现它过于繁琐。 SpacCy 很棒,Gensim 很棒。但我正在使用 scikit-learn。我的代码与示例略有不同,因为它显示了幕后发生的一些事情。

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer(lowercase=True, tokenizer=None, stop_words='english',
   analyzer='word', max_df=1.0, min_df=1, max_features=None)
count_vect.fit(your training data)

# uncomment if you'd like to know the mapping of the columns to the words.
# count_vect.vocabulary_
# for key in sorted(count_vect.vocabulary_.keys()):
#     print("{0:<20s} {1}".format(key, count_vect.vocabulary_[key]))

关于训练集:

X_train_counts = count_vect.transform(your training data)
print("The type of X_train_counts is {0}.".format(type(X_train_counts)))
print("The X matrix has {0} rows (documents) and {1} columns (words).".format(
        X_train_counts.shape[0], X_train_counts.shape[1]))

那会给你这样的东西:

The type of X_train_counts is <class 'scipy.sparse.csr.csr_matrix'>.
The X matrix has 2257 rows (documents) and 35482 columns (words).

4。将它们转换为频率(tf 或 tf-idf)。

您出现了单词。 CountVectorizer 只是每个单词在每个文档中出现的次数。对于每个文档,我们希望按单词数进行归一化。这是术语(或单词)频率。 IDF 可用于避免将出现的单词除以庞大的单词数据集而导致的下溢错误。这在你的情况下不是真的,但通常是一个问题。

5。好的,现在你可以开始训练分类器了。

坚持使用 Scikit 学习示例,至少现在是这样。他们使用的是朴素贝叶斯,我阐述了为什么我认为 Lasso 和 Ridge 不适合这种情况的原因。但是如果你想使用回归模型,你也已经准备好了。如果您想添加其他字段(符号、年龄等),您可以考虑将它们附加到每条记录。

此时我还有几个步骤:

6。找出与每个类别关联的标记作为嗅探测试。

总的来说,选择类别和与每个类别相关的词是一门艺术。您可能需要对此进行迭代。

feature_words = count_vect.get_feature_names()
n = 7 #number of top words associated with the category that we wish to see

for cat in range(len(categories)):
    print(f"\nTarget: {cat}, name: {target_names[cat]}")
    log_prob = nb_model.feature_log_prob_[cat]
    i_topn = np.argsort(log_prob)[::-1][:n]
    features_topn = [feature_words[i] for i in i_topn]
    print(f"Top {n} tokens: ", features_topn)

7。预测作为第二次嗅探测试。

您编造的一到三个新文档脱离了类似的分类。那么:

X_new_counts = count_vect.transform(docs_new)
X_new_tfidf = tfidf_transformer.transform(X_new_counts)
predictions = nb_model.predict(X_new_tfidf)
print('Predictions')
for doc, category in zip(docs_new, predictions):
    print("{0} => {1}".format(doc, twenty_train.target_names[category]))

8 & 9. 教程中完成的流水线和评估。

来自后续问题的更多信息。

  • 您问题的某些部分的答案已包含在上面。
  • Train-test 在标记化之前,反之亦然。我在这里仔细选择了我的话,所以请仔细阅读这部分。目前,最好的做法是先拆分然后标记化。基本原理是可重复性。其他人标记然后分裂。基本原理是计算效率,术语 freq 对于两者都是相同的。您会看到两者都已完成 一直。数据科学家会对其进行广泛的测试。
  • 输出会是什么样子?这取决于你的问题是什么,你处于什么阶段,以及你如何编码。你似乎只是在做垃圾邮件过滤器。在某些时候,您将有一组加载,通常采用 word: tf-idf 加载的形式,每个文档有几个 terms/loadings。您可能或可能已经设置了一个阈值,因此除了模型的概率结果之外,您只能看到过滤结果。
  • 其他列呢?正如我之前所说,'Label' 很关键,因为这是有监督的学习。年龄是无用的;名称可能没有用除非所有名称都是唯一的。有趣的事实:Whosebug 上大约有 150 个名为 'math' 或 'Math'。大概只有一个有您的用户号码。 'Symbol' 很棘手,你应该好好想想。

最后一点。这是一个独立的领域是有原因的。上面写书和上面有多个文章系列是有原因的。因此,将它塞进一堵墙的文字中,虽然简洁,但可能 sub-optimal 因为有太多您需要了解的内容未包含在内。