内容简介:原文标题:A simple way to anonymize data with Python and Pandas原文链接 https://dev.to/r0f1/a-simple-way-to-anonymize-data-with-python-and-pandas-79g作者:Florian Rohrer
原文标题:A simple way to anonymize data with Python and Pandas
原文链接 https://dev.to/r0f1/a-simple-way-to-anonymize-data-with-python-and-pandas-79g
作者:Florian Rohrer
译者:大邓
最近,我收到了一个数据集,其中包含有关客户的敏感信息,而这些敏感信息是不能公开的。 数据集位于我们的一台服务器上,我认为这是一个相当安全的位置。 我想将数据复制到我的本地驱动,以便更舒适地处理数据,同时不必担心数据脱敏信息被泄露。 所以,我写了一个改变数据的小脚本用来将敏感数据进行脱敏华操作。 我将详细介绍我所采取的所有步骤,并重点介绍一些方便的技巧。
任务
任务是准备一个数据集,以便以后可以用于机器学习目的(例如分类,回归,聚类)而不包含任何敏感信息。 最终数据集不应与原始数据集有太大差异,应反映初始数据集的分布。我将使用Jupyter笔记本作为我的环境。
首先,让我们导入所有必要的库。
import pandas as pd
import numpy as np
import scipy.stats
%matplotlib inline
import matplotlib.pyplot as plt
from sklearn_pandas import DataFrameMapper
from sklearn.preprocessing import LabelEncoder
# get rid of warnings
import warnings
warnings.filterwarnings("ignore")
# 对每个jupyter cell能得到超过1个的输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
from utils import best_fit_distribution,plot_result
我将假设您已熟悉此处使用的大多数库。 我只想强调三件事:
-
sklearn_pandas是一个方便的库,试图弥合两个包之间的差距。 它提供了一个DataFrameMapper类,它使得处理pandas.DataFrame更容易,它能让我们用更少的代码对数据进行重新编码。 -
来自IPython.core.interactiveshell…我更改了一个Jupyter Notebook默认值,这样就显示了多个输出。 关于其他方便的Jupyter技巧的一篇很好的博客文章就在这里。
-
最后,我们将把一些代码(best_fit_distribution,plot_result)放入一个名为utils.py的文件中,我们将放在Notebook旁边。
对于我们的分析,我们将使用 泰坦尼克数据集的titanic_train.csv
df = pd.read_csv("titanic_train.csv")
df.shape
df.head()
删除脱敏信息列
现在已经加载了数据,我们将删除所有可识别个人身份的信息。 列 [“PassengerId”,“Name”] 包含此类信息。
请注意, [“PassengerId”,“Name”] 对于每一行都是唯一的,因此如果我们构建机器学习模型,我们将在以后删除它们。
可以对 [“Ticket”,“Cabin”] 进行类似的论证,这几乎对于每一行都是唯一的。
在本文中为了方便,我们不会处理缺失值。 我们只是忽略所有包含缺失值的观察结果(如果行数据全为空,删除该行)。
df.drop(columns=["PassengerId", "Name"], inplace=True) df.drop(columns=["Ticket", "Cabin"], inplace=True) df.dropna(inplace=True) df.shape df.head()
类别型数据编码
随后,我们需要将 Sex 和 Embarked 这种类别型数据编码数字。
其中 Sex 编码为0和1, Embarked 编码为0,1,2。
LabelEncoder() 是类别型数据编码为数字的常用类。
encoders = [(["Sex"], LabelEncoder()), (["Embarked"], LabelEncoder())] mapper = DataFrameMapper(encoders, df_out=True) new_cols = mapper.fit_transform(df.copy()) df = pd.concat([df.drop(columns=["Sex", "Embarked"]), new_cols], axis="columns") df.shape df.head()
sklearn_pandas 库的 DataFrameMapper 类会接收元组组成的列表,其中元组的第一个元素是列名,元组的第二个元素是转换器(transformer)。类似的转换器还有MinMaxScaler()、StandardScaler()、FunctionTransformer()。
在最后一行代码中,我们将编码后的数据与剩下的数据合并起来。注意,axis参数可以传 axis=1 ,也可以 axis='columns' ,但是推荐大家使用后一种方法,这样能增加代码的可读性。
#查看每个列名(字段)有几种值 df.nunique()
Survived 2 Pclass 3 Age 88 SibSp 6 Parch 7 Fare 219 Sex 2 Embarked 3 dtype: int64
离散型变量的处理
在上一面代码中我们打印了每个 列名(字段) 有几种值。一般这个变量的值的种类如果比较多,往往将该列(字段)定义为连续型变量;而当这个变量含有较少种类的值,往往将其定义为离散型变量。
在本文中,我们将阈值设置为20。当变量的值的种类大于20,定义为 连续型变量 。反之,定义为 离散型变量
import pandas as pd seires = pd.Series([1,2,1,1,1, 2]) seires.nunique() #结果是2
nunique()用来查看序列中有几种值
返回2,意思是series中只有两种值
categorical = [] #离散
continuous = [] #连续
#对列名进行迭代
for c in df.columns.tolist():
col = df[c]
nunique = col.nunique()
if nunique < 20:
categorical.append(c)
else:
continuous.append(c)
categorical
查看categorical内容,返回
['Survived', 'Pclass', 'SibSp', 'Parch', 'Sex', 'Embarked']
continuous
['Age', 'Fare']
numpy.random.choice(a, size=None, p=None)
首先我们决定每个字段(列名)作为变量,之后我们使用这个变量的概率函数,并将概率函数传入np.random.choice()来创建一个与原始数据分布类似的概率函数。在这里我们假设每个变量仅有5条记录。
for c in categorical:
counts = df[c].value_counts()
np.random.choice(list(counts.index), p=(counts/len(df)).values, size=5)
运行结果
array([1, 1, 0, 1, 1])
array([3, 3, 3, 1, 1])
array([0, 1, 0, 0, 0])
array([0, 0, 0, 0, 0])
array([0, 1, 0, 0, 1])
array([0, 1, 2, 2, 0])
连续型变量的处理
很幸运,连续型变量的处理方法已经在
stackoverflow thread找到,计算方法大致如下:
-
使用预先设定的多个数字间隔来创建一个hist直方图
-
使用一系列连续型分布函数,逐个运算匹配hist直方图。这个过程也会给分布函数生成参数
-
直到某个分布函数使得数据与hist拟合的最好,拥有最小的残差平方和。此时,该分布函数就是数据的分布函数。
具体代码可见utils.py文件。连续型变量只有两个变量,分别是Age和Fare
best_distributions = []
for c in continuous:
data = df[c]
best_fit_name, best_fit_params = best_fit_distribution(data, 50)
best_distributions.append((best_fit_name, best_fit_params))
best_distributions
[('fisk', (11.744665309421649, -66.15529969956657, 94.73575225186589)),
('halfcauchy', (-5.537941926133496e-09, 17.86796415175786))]
Age变量是fisk分布,Fare变量是halfcauchy分布
best_distributions = [
('fisk', (11.744665309421649, -66.15529969956657, 94.73575225186589)),
('halfcauchy', (-5.537941926133496e-09, 17.86796415175786))]
plot_result(df, continuous, best_distributions)
现在我们将上面所有的过程封装成一个函数generate_like_df
def generate_like_df(df, categorical_cols, continuous_cols, best_distributions, n, seed=0):
np.random.seed(seed)
d = {}
for c in categorical_cols:
counts = df[c].value_counts()
d[c] = np.random.choice(list(counts.index), p=(counts/len(df)).values, size=n)
for c, bd in zip(continuous_cols, best_distributions):
dist = getattr(scipy.stats, bd[0])
d[c] = dist.rvs(size=n, *bd[1])
return pd.DataFrame(d, columns=categorical_cols+continuous_cols)
gendf = generate_like_df(df, categorical, continuous, best_distributions, n=100) gendf.shape gendf.head()
往期文章
以上所述就是小编给大家介绍的《[译] 使用 sklearn 和 pandas 库对敏感数据进行匿名化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective JavaScript
David Herman / Addison-Wesley Professional / 2012-12-6 / USD 39.99
"It's uncommon to have a programming language wonk who can speak in such comfortable and friendly language as David does. His walk through the syntax and semantics of JavaScript is both charming and h......一起来看看 《Effective JavaScript》 这本书的介绍吧!