如何从两列DataFrame构建DNS查询出现情况的0-1稀疏矩阵
请问如何从仅包含两列的DataFrame中构建记录值出现情况的矩阵?我目前正在处理DNS数据集,共包含100个JSON文件,每个文件对应单日的DNS流量数据。我需要提取自身关注的IP地址子集,构建记录其出现情况的0-1稀疏矩阵,数据集中共有8个待分析的真实IP地址。
由于我仅关注s_ip(源IP地址)和dns_query(发起的DNS查询请求)两列字段,我已基于这两个字段以及目标IP列表['10.0.0.31','10.0.0.60','10.0.0.33','10.0.0.32','10.0.0.42','10.0.0.44','10.0.0.34','10.0.0.29']完成数据集过滤,也得到了列表中第一个IP对应的过滤后数据子集。
我尝试使用CountVectorizer和HashVectorizer实现需求但未成功。我目标构建的稀疏矩阵中,行对应单日的单个关注IP地址,列对应当日JSON文件中出现的所有dns_query取值,矩阵元素代表对应DNS查询的出现情况,效果类似如下sklearn官方示例中以文本为语料构建的词频矩阵:
In [7]: from sklearn.feature_extraction.text import CountVectorizer In [8]: corpus = [ ...: 'This is the first document.', ...: 'This document is the second document.', ...: 'And this is the third one.', ...: 'Is this the first document?',] In [9]: vectorizer = CountVectorizer() In [10]: X = vectorizer.fit_transform(corpus) In [11]: vectorizer.get_feature_names_out() Out[11]: array(['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this'], dtype=object) In [12]: print(X.toarray()) [[0 1 1 1 0 0 1 0 1] [0 2 0 1 0 1 1 0 1] [1 0 0 1 1 0 1 1 1] [0 1 1 1 0 0 1 0 1]] In [13]:
我已在Colab平台提供了该问题的最小可运行示例(MWE),恳请提供相关实现思路与指导。
CountVectorizer适配失败的核心原因是:它默认处理空格分隔的自由文本,会自动执行分词逻辑,而你手里已经是结构化的两列DataFrame,不需要走文本分词流程,直接用结构化方法实现即可,两种可直接运行的方案如下:
方案1:pandas原生交叉表实现(逻辑最直观,无额外依赖)
直接通过交叉表生成行、列、值完全匹配需求的矩阵,支持一键转稀疏格式:
import pandas as pd # 假设已加载合并所有单日数据为总DataFrame,包含date(日期标识)、s_ip、dns_query三列 target_ips = ['10.0.0.31','10.0.0.60','10.0.0.33','10.0.0.32','10.0.0.42','10.0.0.44','10.0.0.34','10.0.0.29'] df_filtered = df[df['s_ip'].isin(target_ips)].copy() # 生成0-1矩阵前先对「单日+单IP+单查询」去重,避免重复计数 df_dedup = df_filtered.drop_duplicates(subset=['date', 's_ip', 'dns_query']) # 生成交叉表 occur_matrix = pd.crosstab( index=[df_dedup['date'], df_dedup['s_ip']], # 行维度:单日+单个关注IP columns=df_dedup['dns_query'], # 列维度:所有出现过的DNS查询 values=1, aggfunc='count', fill_value=0 ) # 转scipy稀疏矩阵供后续建模/计算使用 sparse_matrix = occur_matrix.sparse.to_coo().tocsr() # 获取列标签(所有dns_query取值) query_cols = occur_matrix.columns.to_numpy() # 获取行标签(日期+IP的组合) row_labels = occur_matrix.index.to_list()
如果仅需处理单日单个JSON文件,去掉index里的date字段即可,行直接对应单个关注IP。
方案2:sklearn生态MultiLabelBinarizer实现(适合大样本稀疏场景)
如果数据量较大,希望直接输出sklearn兼容的稀疏矩阵,可以用多标签二值化编码器实现:
from sklearn.preprocessing import MultiLabelBinarizer # 按行维度分组,将每个组对应的dns_query聚合为列表 grouped = df_dedup.groupby(['date', 's_ip'])['dns_query'].apply(list).reset_index() # 初始化二值化编码器,直接输出稀疏矩阵 mlb = MultiLabelBinarizer(sparse_output=True) sparse_matrix = mlb.fit_transform(grouped['dns_query']) # 获取列标签 query_cols = mlb.classes_ # 获取行标签 row_labels = grouped[['date', 's_ip']].to_numpy()
注意:如果你需要统计查询出现频次而非是否出现的0-1值,跳过drop_duplicates去重步骤即可,交叉表会自动完成计数,输出效果和你贴的CountVectorizer词频矩阵完全一致。
常见踩坑提醒:不要试图把单个IP对应的所有dns_query拼接成空格分隔的字符串喂给CountVectorizer——如果dns_query(比如域名)包含特殊字符、点号甚至空格,默认分词器会把完整域名拆成碎块,最终得到的列不是完整的查询值,结果完全不可用。
内容的提问来源于stack exchange,提问作者Geovani Benita

