【问题标题】:Column string conversion based on unique values基于唯一值的列字符串转换
【发布时间】:2018-11-20 12:33:12
【问题描述】:

有没有办法用 Python 中的有序数字替换二维数组列中的字符串值?

例如说你有一个二维数组:

a = np.array([['A',0,'C'],['A',0.3,'B'],['D',1,'D']])
a
Out[57]: 
array([['A', '0', 'C'],
       ['A', '0.3', 'B'],
       ['D', '1', 'D']], dtype='<U3')

如果我想用数字 0,0,1 和 'C','B','D' 替换第一列中的字符串值 'A','A','D' 和 0,1 ,2 有没有一种有效的方法。

了解一下可能会有所帮助:

  • 不同列中的替换编号与列无关。即,字符串被数字替换的每一列都将从 0 开始,并增加到该列中唯一值的数量。
  • 以上是一个测试用例,实际数据更大,字符串列更多。

这是我快速想出的解决此问题的示例方法:

for  j in range(a.shape[1]):
    b = list(set(a[:,j]))
    length = len(b)
    for i in range(len(b)):
        indices = np.where(a[:,j]==b[i])[0]
        print(indices)
        a[indices,j]=i

然而,这似乎是一种低效的实现方式,并且也无法区分列中的浮点值或字符串值,并且默认使用数字字符串替换值:

a
Out[91]: 
array([['1.0', '0.0', '2.0'],
       ['1.0', '1.0', '0.0'],
       ['0.0', '2.0', '1.0']], dtype='<U3')

对于此事的任何帮助将不胜感激!

【问题讨论】:

    标签: python arrays string numpy 2d


    【解决方案1】:

    您似乎正在尝试执行label encoding

    我可以想到两个选项:pandas.factorizesklearn.preprocessing.LabelEncoder

    使用LabelEncoder

    from sklearn.preprocessing import LabelEncoder
    
    b = np.zeros_like(a, np.int) 
    for column in range(a.shape[1]):
        b[:, column] = LabelEncoder().fit_transform(a[:, column])
    

    那么b 将是:

    array([[0, 0, 1],
           [0, 1, 0],
           [1, 2, 2]])
    

    如果您希望能够返回原始值,则需要保存编码器。你可以这样做:

    from sklearn.preprocessing import LabelEncoder
    
    encoders = {}
    b = np.zeros_like(a, np.int)
    for column in range(a.shape[1]):
        encoders[column] = LabelEncoder()
        b[:, column] = encoders[column].fit_transform(a[:, column])
    

    现在encoders[0].classes_ 将拥有:

    array(['A', 'D'], dtype='<U3')
    

    这意味着“A”被映射到0,“D”被映射到1

    最后,如果你进行编码覆盖a而不是使用新矩阵c,你将获得整数作为字符串("1"而不是1),你可以用astype(int解决这个问题) :

    encoders = {}
    for column in range(a.shape[1]):
        encoders[column] = LabelEncoder()
        a[:, column] = encoders[column].fit_transform(a[:, column])
    
    # At this point, a will have strings instead of ints because a had type str
    # array([['0', '0', '1'],
    #       ['0', '1', '0'],
    #       ['1', '2', '2']], dtype='<U3')
    
    a = a.astype(int)
    
    # Now `a` is of type int
    # array([[0, 0, 1],
    #        [0, 1, 0],
    #        [1, 2, 2]])
    

    使用pd.factorize

    factorize返回编码列和编码映射,所以如果你不关心它,你可以避免保存它:

    for column in range(a.shape[1]):
        a[:, column], _ = pd.factorize(a[:, column]) # Drop mapping
    
    a = a.astype(int) # same as above, it's of type str
    # a is
    # array([[0, 0, 1],
    #        [0, 1, 0],
    #        [1, 2, 2]])
    

    如果要保留编码映射:

    mappings = []
    for column in range(a.shape[1]):
        a[:, column], mapping = pd.factorize(a[:, column])
        mappings.append(mapping)
    
    a = a.astype(int)
    

    现在mappings[0] 将有以下数据:

    array(['A', 'D'], dtype=object)
    

    与 sklearn 的 LabelEncoder 解决方案的 encoders[0].classes_ 具有相同的语义。

    【讨论】:

    • 非常感谢!我最终选择了你的标签编码器方法。感谢您抽出宝贵时间回复
    • 很高兴为您提供帮助:)
    • 如果我可以扩展这个问题,有没有办法将 label_encoder 与预定义的唯一值数组结合使用?我问的原因是因为我想在几个巨大的数据集上执行这种编码,这些数据集有一些常见的字符串值和一些不常见的值,因此上述方法会在不同的数据集中给相同的字符串一个不同的值,而我需要对它们进行编码我计划加载的所有数据集都相同。
    • 是否可以将列的所有可能值放在一起?如果是,您可以执行以下操作:首先,使用方法fit:enc = LabelEncoder(); enc.fit(all_possible_values) 在该列的所有值上拟合 LabelEncoder。这将在enc 中创建您想要的唯一映射。之后,您只需使用column = enc.transform(column) 转换每个特定数据帧的列,即可获得所需的跨数据帧一致性。
    • 获得所有唯一值当然是可能的,所以我认为这应该可行。再次感谢,我试试看!一旦编码适合以后使用的数据,是否有一种明智的方法来保存编码?这样就不必在一个长时间的脚本运行中完成该过程
    【解决方案2】:

    您可以使用 Numpy 以高效的方式做您想做的事。

    基本上,您迭代输入的每一列中的值,同时跟踪在集合或字典中观察到的字母。这与您已经拥有的类似,但效率稍高(您避免调用np.where 一方面)。

    这里有一个函数 charToIx 可以做你想做的事:

    from collections import defaultdict
    from string import ascii_letters
    
    class Ix:
        def __init__(self):
            self._val = 0
    
        def __call__(self):
            val = self._val
            self._val += 1
            return val
    
    def charToIx(arr, dtype=None, out=None):
        if dtype is None:
            dtype = arr.dtype
    
        if out is None:
            out = np.zeros(arr.shape, dtype=dtype)
    
        for incol,outcol in zip(arr.T, out.T):
            ix = Ix()
            cixDict = defaultdict(lambda: ix())
            for i,x in enumerate(incol):
                if x in cixDict or x in ascii_letters:
                    outcol[i] = cixDict[x]
                else:
                    outcol[i] = x
    
        return out
    

    您在调用函数时指定输出数组的类型。所以输出:

    a = np.array([['A',0,'C'],['A',0.3,'B'],['D',1,'D']])
    print(charToIx(a, dtype=float))
    

    将是一个float 数组:

    array([[0. , 0. , 0. ],
           [0. , 0.3, 1. ],
           [1. , 1. , 2. ]])
    

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多