规范化熊猫数据框的列

Posted

技术标签:

【中文标题】规范化熊猫数据框的列【英文标题】:Normalize columns of pandas data frame 【发布时间】:2014-12-12 10:34:24 【问题描述】:

我在 pandas 中有一个数据框,其中每一列都有不同的值范围。例如:

df:

A     B   C
1000  10  0.5
765   5   0.35
800   7   0.09

知道如何规范这个数据框的列,其中每个值都在 0 和 1 之间?

我想要的输出是:

A     B    C
1     1    1
0.765 0.5  0.7
0.8   0.7  0.18(which is 0.09/0.5)

【问题讨论】:

有一个应用函数,例如frame.apply(f, axis=1) 其中 f 是一个对行执行某些操作的函数... 归一化可能不是最合适的措辞,因为scikit-learn documentation 将其定义为“将单个样本缩放为具有单位范数的过程”(即逐行,如果我没看错)。 我不明白,为什么 min_max 缩放被认为是标准化!正态必须具有均值为零和方差为 1 的正态分布的意义。 如果您在 2020 年或以后访问此问题,请查看@Poudel 的答案,如果您使用 pandas 与 sklearn,您会得到不同的标准化答案。 @Poudel 这是因为ddof 参数吗? 【参考方案1】:

您可以使用带有 lambda 的 apply 函数来加快处理速度:

def normalize(df_col):

  # Condition to exclude 'ID' and 'Class' feature
  if (str(df_col.name) != str('ID') and str(df_col.name)!=str('Class')):
        max_value = df_col.max()
        min_value = df_col.min()

        #It avoids NaN and return 0 instead
        if max_value == min_value:
          return 0

        sub_value = max_value - min_value
        return np.divide(np.subtract(df_col,min_value),sub_value)
  else:
        return df_col

 df_normalize = df.apply(lambda x :normalize(x))

【讨论】:

【参考方案2】:

请注意这个答案,因为它仅适用于范围为 [0, n] 的数据。这不适用于任何范围的数据。


简单就是美丽:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()

【讨论】:

请注意,OP 要求 [0..1] 范围,此解决方案可扩展到 [-1..1] 范围。用数组 [-10, 10] 试试这个。 @AlexanderSosnovshchenko 不是真的。 Basil Musa 假设 OP 的矩阵总是非负的,这就是他给出这个解决方案的原因。如果某列有一个负条目,则此代码不会标准化为 [-1,1] 范围。尝试使用数组 [-5, 10]。 Cina 的回答 df["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min()) 给出了用负值归一化为 [0,1] 的正确方法 也许更简单:df /= df.max() - 假设目标是分别对每一列进行规范化。 这个答案是错误的。这里不能做出非否定的假设,因为不是 OP 而不是未来的读者所说的。此外,即使是严格的正数在这里也不起作用:[1, 10] 将被规范化为 [0.1, 1] 而不是 [0,1] 谢谢@Gulzar,我是这个答案的作者,TBH 我很惊讶它被投票了 29 次。【参考方案3】:

归一化方法的详细示例

Pandas 归一化(无偏) Sklearn 标准化(有偏差) 有偏见与无偏见会影响机器学习吗? 最大混合缩放

参考资料: Wikipedia: Unbiased Estimation of Standard Deviation

示例数据

import pandas as pd
df = pd.DataFrame(
               'A':[1,2,3],
               'B':[100,300,500],
               'C':list('abc')
             )
print(df)
   A    B  C
0  1  100  a
1  2  300  b
2  3  500  c

使用 pandas 进行归一化(给出无偏估计)

当归一化时,我们只需减去均值并除以标准差。

df.iloc[:,0:-1] = df.iloc[:,0:-1].apply(lambda x: (x-x.mean())/ x.std(), axis=0)
print(df)
     A    B  C
0 -1.0 -1.0  a
1  0.0  0.0  b
2  1.0  1.0  c

使用 sklearn 进行归一化(给出有偏估计,与 pandas 不同)

如果你对sklearn 做同样的事情,你会得到不同的输出!

import pandas as pd

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()


df = pd.DataFrame(
               'A':[1,2,3],
               'B':[100,300,500],
               'C':list('abc')
             )
df.iloc[:,0:-1] = scaler.fit_transform(df.iloc[:,0:-1].to_numpy())
print(df)
          A         B  C
0 -1.224745 -1.224745  a
1  0.000000  0.000000  b
2  1.224745  1.224745  c

sklearn 的偏差估计是否会降低机器学习的功能?

没有。

sklearn.preprocessing.scale 的官方文档指出,使用有偏估计器不太可能影响机器学习算法的性能,我们可以安全地使用它们。

来自官方文档:

我们对标准差使用有偏估计量,相当于numpy.std(x, ddof=0)。请注意,ddof 的选择不太可能影响模型性能。

MinMax 缩放呢?

在 MinMax 缩放中没有标准偏差计算。所以 pandas 和 scikit-learn 的结果是一样的。

import pandas as pd
df = pd.DataFrame(
               'A':[1,2,3],
               'B':[100,300,500],
             )
(df - df.min()) / (df.max() - df.min())
     A    B
0  0.0  0.0
1  0.5  0.5
2  1.0  1.0


# Using sklearn
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
arr_scaled = scaler.fit_transform(df) 

print(arr_scaled)
[[0.  0. ]
 [0.5 0.5]
 [1.  1. ]]

df_scaled = pd.DataFrame(arr_scaled, columns=df.columns,index=df.index)
print(df_scaled)
     A    B
0  0.0  0.0
1  0.5  0.5
2  1.0  1.0

【讨论】:

【参考方案4】:

如果你的数据是正偏态的,最好的标准化方法是使用对数转换:

df = np.log10(df)

【讨论】:

【参考方案5】:
df_normalized = df / df.max(axis=0)

【讨论】:

【参考方案6】:

使用 Pandas 的一种简单方法:(这里我想使用均值归一化)

normalized_df=(df-df.mean())/df.std()

使用最小-最大标准化:

normalized_df=(df-df.min())/(df.max()-df.min())

编辑:为了解决一些问题,需要说 Pandas 在上面的代码中自动应用了 colomn-wise 函数。

【讨论】:

可以通过窗口功能以某种方式完成吗?我的意思是根据最近 10 次观察计算 max() 和 min()。 如果你想保存一些列 - 做normalized_df['TARGET'] = df['TARGET'] 这是一个很好的解决方案,但您需要进行大量不太美观的检查以避免除以零错误 提出一个新问题;如果有人知道答案请赐教:***.com/questions/61726904/… 是否有一种内置的标准方法可以在不遍历所有列的情况下按列执行此操作?【参考方案7】:

默认情况下,Pandas 会按列进行标准化。试试下面的代码。

X= pd.read_csv('.\\data.csv')
X = (X-X.min())/(X.max()-X.min())

输出值将在 0 和 1 的范围内。

【讨论】:

【参考方案8】:

您可以通过这种方式简单地使用 pandas.DataFrame.transform1 函数:

df.transform(lambda x: x/x.max())

【讨论】:

如果所有值都是负数,此解决方案将不起作用。考虑 [-1, -2, -3]。我们除以 -1,现在我们有 [1,2,3]。 正确处理负数:df.transform(lambda x: x / abs(x).max())【参考方案9】:

您可以使用包 sklearn 及其相关的预处理实用程序来规范化数据。

import pandas as pd
from sklearn import preprocessing

x = df.values #returns a numpy array
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
df = pd.DataFrame(x_scaled)

有关更多信息,请查看 scikit-learn documentation 关于预处理数据:将特征缩放到一个范围。

【讨论】:

我认为这将摆脱列名,这可能是 op 首先使用数据帧的原因之一。 这将规范行而不是列,除非你先转置它。做 Q 要求的事情:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index) @pietz 保留列名,请参阅this post。基本上将最后一行替换为 df=pandas.DataFrame(x_scaled, columns=df.columns) @hobs 这是不正确的。 Sandman 的代码按列和按列标准化。如果你转置,你会得到错误的结果。 @petezurich 看起来 Sandman 或 Praveen 更正了他们的代码。不幸的是,无法纠正 cmets ;)【参考方案10】:

这只是简单的数学。答案应该如下所示。

normed_df = (df - df.min()) / (df.max() - df.min())

【讨论】:

【参考方案11】:

这就是你如何使用列表理解按列进行操作:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]

【讨论】:

【参考方案12】:

您可能希望对某些列进行规范化,而其他列则保持不变,就像数据标签或分类列不变的一些回归任务一样,所以我建议您采用这种 Python 方式(这是@shg 和 @Cina 答案的组合) :

features_to_normalize = ['A', 'B', 'C']
# could be ['A','B'] 

df[features_to_normalize] = df[features_to_normalize].apply(lambda x:(x-x.min()) / (x.max()-x.min()))

【讨论】:

【参考方案13】:

您可以在一行中完成此操作

DF_test = DF_test.sub(DF_test.mean(axis=0), axis=1)/DF_test.mean(axis=0)

它对每一列取平均值,然后从每一行中减去它(平均值)(特定列的平均值仅从其行中减去)并仅除以平均值。最后,我们得到的是归一化的数据集。

【讨论】:

【参考方案14】:

以下函数计算 Z 分数:

def standardization(dataset):
  """ Standardization of numeric fields, where all values will have mean of zero 
  and standard deviation of one. (z-score)

  Args:
    dataset: A `Pandas.Dataframe` 
  """
  dtypes = list(zip(dataset.dtypes.index, map(str, dataset.dtypes)))
  # Normalize numeric columns.
  for column, dtype in dtypes:
      if dtype == 'float32':
          dataset[column] -= dataset[column].mean()
          dataset[column] /= dataset[column].std()
  return dataset

【讨论】:

【参考方案15】:

您可以创建要规范化的列列表

column_names_to_normalize = ['A', 'E', 'G', 'sadasdsd', 'lol']
x = df[column_names_to_normalize].values
x_scaled = min_max_scaler.fit_transform(x)
df_temp = pd.DataFrame(x_scaled, columns=column_names_to_normalize, index = df.index)
df[column_names_to_normalize] = df_temp

您的 Pandas 数据框现在仅在您想要的列处进行规范化


但是,如果您想要相反,请选择您想要规范化的列列表,您可以简单地创建所有列的列表并删除不需要的列

column_names_to_not_normalize = ['B', 'J', 'K']
column_names_to_normalize = [x for x in list(df) if x not in column_names_to_not_normalize ]

【讨论】:

【参考方案16】:
def normalize(x):
    try:
        x = x/np.linalg.norm(x,ord=1)
        return x
    except :
        raise
data = pd.DataFrame.apply(data,normalize)

从pandas的文档来看,DataFrame结构可以对自身应用一个操作(函数)。

DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)

沿 DataFrame 的输入轴应用函数。 传递给函数的对象是具有 DataFrame 索引(axis=0)或列(axis=1)索引的 Series 对象。返回类型取决于传递的函数是否聚合,或者如果 DataFrame 为空则 reduce 参数。

您可以应用自定义函数来操作 DataFrame 。

【讨论】:

最好解释一下为什么你的代码解决了 OPs 问题,这样人们就可以调整策略而不仅仅是复制你的代码。请阅读How do I write a good answer?【参考方案17】:

Sandman 和 Praveen 给出的解决方案非常好。唯一的问题是,如果您的数据框的其他列中有分类变量,则此方法将需要一些调整。

我对此类问题的解决方案如下:

 from sklearn import preprocesing
 x = pd.concat([df.Numerical1, df.Numerical2,df.Numerical3])
 min_max_scaler = preprocessing.MinMaxScaler()
 x_scaled = min_max_scaler.fit_transform(x)
 x_new = pd.DataFrame(x_scaled)
 df = pd.concat([df.Categoricals,x_new])

【讨论】:

这个答案很有用,因为互联网上的大多数示例都将一个缩放器应用于所有列,而这实际上解决了一个缩放器(例如 MinMaxScaler)不应应用于所有列的情况。【参考方案18】:

如果你喜欢使用 sklearn 包,可以使用 pandas loc 来保留列名和索引名,如下所示:

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
scaled_values = scaler.fit_transform(df) 
df.loc[:,:] = scaled_values

【讨论】:

【参考方案19】:

您的问题实际上是作用于列的简单转换:

def f(s):
    return s/s.max()

frame.apply(f, axis=0)

或者更简洁:

   frame.apply(lambda x: x/x.max(), axis=0)

【讨论】:

lambda 是最好的 :-) 这不应该是axis=1,因为问题是按列标准化? 不,来自docs:axis [...] 0 or 'index': apply function to each column。默认值实际上是axis=0,所以这个单行代码可以写得更短:-) 谢谢@tschm。 只有当最小值为 0 时才正确,这不是你应该真正假设的东西 我的示例旨在说明如何在数据框列上应用函数。显然,与往常一样,您需要注意极端情况,例如这里最大值可能为零并导致问题。不确定我是否理解@QFSW。【参考方案20】:

根据这篇文章:https://stats.stackexchange.com/questions/70801/how-to-normalize-data-to-0-1-range

您可以执行以下操作:

def normalize(df):
    result = df.copy()
    for feature_name in df.columns:
        max_value = df[feature_name].max()
        min_value = df[feature_name].min()
        result[feature_name] = (df[feature_name] - min_value) / (max_value - min_value)
    return result

您无需担心自己的价值观是消极的还是积极的。并且值应该很好地分布在 0 和 1 之间。

【讨论】:

注意当最小值和最大值相同时,你的分母是 0,你会得到一个 NaN 值。 @HrushikeshDhumal,不需要标准化,因为所有值都是相等的。 @AppajiChintimi,此解决方案适用于整个数据,如果您尚未进行完整性检查,您可能会遇到麻烦。【参考方案21】:

我认为在 pandas 中做到这一点的更好方法就是

df = df/df.max().astype(np.float64)

编辑如果您的数据框中存在负数,您应该改用

df = df/df.loc[df.abs().idxmax()].astype(np.float64)

【讨论】:

如果一列的所有值都为零,这将不起作用 将当前值除以最大值不会得到正确的归一化,除非最小值为 0。 我同意,但这正是旧约所要求的(见他的例子)

以上是关于规范化熊猫数据框的列的主要内容,如果未能解决你的问题,请参考以下文章

合并熊猫数据框的列时出错

对熊猫数据框的列应用差异[重复]

首先按最大负值对熊猫数据框的列进行排序

如何将熊猫数据框的列设置为列表

如何迭代熊猫数据框的列以运行回归

熊猫数据框的索引目前是两列的“分组依据”,但我希望它们作为自己的列,并带有标准数字索引