创建字符串在数据帧中是否存在的二进制表示

2024-09-27 00:20:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个由多个列组成的数据帧,这些列的单元格可能包含字符串,也可能不包含字符串。例如:

import numpy as np
import pandas as pd

df = pd.DataFrame({'A':['asfe','eseg','eesg','4dsf','','hdt','gase','gex','gsges','hhbr'],
                   'B':['','bdb','htsdg','','rdshg','th','tjf','','',''],
                   'C':['hrd','jyf','sef','hdsr','','','','','hdts','aseg'],
                   'D':['','','hdts','afse','nfd','','htf','','',''],
                   'E':['','','','','jftd','','','','jfdt','']})

…看起来像:

       A      B     C     D     E
0   asfe          hrd            
1   eseg    bdb   jyf            
2   eesg  htsdg   sef  hdts      
3   4dsf         hdsr  afse      
4         rdshg         nfd  jftd
5    hdt     th                  
6   gase    tjf         htf      
7    gex                         
8  gsges         hdts        jfdt
9   hhbr         aseg            

我想创建一个列,该列包含一个二进制表示,表示该列是否包含字符串;例如,第一行将表示为10100。你知道吗

我唯一能想到的办法是:

  1. 创建临时数据帧
  2. 逐列检测单元格是否包含 字符并表示为0/1
  3. 将二进制结果串联成单个字符串
  4. 将列从头复制到原始数据帧。你知道吗

这是我创建的代码:

scratchdf = pd.DataFrame().reindex_like(df)

for col in df.columns.values:
    scratchdf[col] = df[col].str.contains(r'\w+',regex = True).astype(int)

scratchdf['bin'] =  scratchdf['A'].astype(str) + \
                    scratchdf['B'].astype(str) + \
                    scratchdf['C'].astype(str) + \
                    scratchdf['D'].astype(str) + \
                    scratchdf['E'].astype(str)

df = df.join(scratchdf['bin'])

…生成最终数据帧:

       A      B     C     D     E    bin
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

这是可行的,但似乎有点浪费(尤其是对于大数据帧)。有没有一种方法可以直接创建二进制表示列,而不需要创建临时数据帧?你知道吗


Tags: 数据字符串dfpdstrastypehdtgase
3条回答

选中空字符串或首先转换为bool,然后转换为intstr,最后转换为joinsum

df['new'] = (df != '').astype(int).astype(str).apply(''.join, axis=1)

#faster alternative
df['new'] = (df != '').astype(int).astype(str).values.sum(axis=1)

print (df)

       A      B     C     D     E    new
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

时间安排:

df = pd.concat([df] * 1000, ignore_index=True)

In [99]: %timeit df.astype(bool).astype(int).astype(str).values.sum(axis=1)
10 loops, best of 3: 155 ms per loop

In [100]: %timeit (df != '').astype(int).astype(str).values.sum(axis=1)
10 loops, best of 3: 158 ms per loop

In [101]: %timeit (df != '').astype(int).astype(str).apply(''.join, axis=1)
1 loop, best of 3: 330 ms per loop

In [102]: %timeit df.astype(bool).astype(int).astype(str).apply(''.join, axis=1)
1 loop, best of 3: 326 ms per loop

In [103]: %timeit df.astype(bool).astype(int).apply(lambda row: ''.join(str(i) for i in row), axis=1)
1 loop, best of 3: 210 ms per loop

可以使用以下事实:空字符串对应于False,非空字符串对应于True。因此,将字符串dataframe转换为bool将给出一个true和false的数据帧。将此重铸为int会将true转换为1,将false转换为0,然后只需跨行应用联接操作:

df['binary'] = df.astype(bool).astype(int).apply(
    lambda row: ''.join(str(i) for i in row), axis=1)
print(df)

结果:

       A      B     C     D     E  binary
0   asfe          hrd              10100
1   eseg    bdb   jyf              11100
2   eesg  htsdg   sef  hdts        11110
3   4dsf         hdsr  afse        10110
4         rdshg         nfd  jftd  01011
5    hdt     th                    11000
6   gase    tjf         htf        11010
7    gex                           10000
8  gsges         hdts        jfdt  10101
9   hhbr         aseg              10100

编辑:刚刚实现另一个用户发布了基本相同的东西(也修复了复制错误)

下面是使用生成器的另一种方法:

def iterable_to_binary_mask(iterable):
    bools = (bool(i) for i in iterable)
    ints = (int(i) for i in bools)
    strs = (str(i) for i in ints)
    return ''.join(strs)

df['binary'] = df.apply(iterable_to_binary_mask, axis=1)

这比我的机器上的类型转换方法慢大约3倍,但是应该使用 最小内存。你知道吗

方法1

a = np.where(df != "", "1", "0").astype("|S1")
df["bin"] = np.apply_along_axis(lambda x: x.tostring().decode("utf-8"), 1, a)

方法2

df["bin"] = np.append(
               np.where(df != "", "1", "0").astype("S1"), 
               np.array([["\n"]]).astype("S1").repeat(df.shape[0], axis=0), 
               axis=1
            ).tostring().decode("utf-8")[:-1].split("\n")

方法2将\n追加到numpy数组的末尾

array([[b'1', b'0', b'1', b'0', b'0', b'\n'],
       [b'1', b'1', b'1', b'0', b'0', b'\n'],
       [b'1', b'1', b'1', b'1', b'0', b'\n'],
       ...,
       [b'1', b'0', b'0', b'0', b'0', b'\n'],
       [b'1', b'0', b'1', b'0', b'1', b'\n'],
       [b'1', b'0', b'1', b'0', b'0', b'\n']], dtype='|S1')

然后调用tostringdecode。删除最后一个“\n”,然后按“\n”拆分。你知道吗

方法3(使用view参考:numpy array of chars to string

np.ascontiguousarray(
    np.where(df != "", "1", "0").astype("S1")
).view('|S5').astype(str)

时间安排:

(Based on jezrael's setup df = pd.concat([df] * 1000, ignore_index=True))

# method 2
%timeit np.append(np.where(df != "", "1", "0").astype("S1"), np.array([["\n"]]).astype("S1").repeat(df.shape[0], axis=0), axis=1).tostring().decode("utf-8")[:-1].split("\n")
12.3 ms ± 175 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# method 3
%timeit np.ascontiguousarray(np.where(df != "", "1", "0").astype("S1")).view('|S5').astype(str)
12.8 ms ± 164 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# method 1 (slower)
%timeit np.apply_along_axis(lambda x: x.tostring().decode("utf-8"), 1, np.where(df != "", "1", "0").astype("S1"))
45 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

耶斯雷尔的复制实验

In [99]: %timeit df.astype(bool).astype(int).astype(str).values.sum(axis=1)
28.9 ms ± 782 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [100]: %timeit (df != '').astype(int).astype(str).values.sum(axis=1)
29 ms ± 645 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [101]: %timeit (df != '').astype(int).astype(str).apply(''.join, axis=1)
168 ms ± 2.93 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [102]: %timeit df.astype(bool).astype(int).astype(str).apply(''.join, axis=1)
173 ms ± 7.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [103]: %timeit df.astype(bool).astype(int).apply(lambda row: ''.join(str(i) for i in row), axis=1)
159 ms ± 3.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

相关问题 更多 >

    热门问题