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

时间:2018-02-27 07:16:39

标签: python python-3.x pandas

我有一个由几个列组成的Pandas数据帧,其中的单元格可能包含也可能不包含字符串。举个例子:

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. 将列从头数据帧复制回原始。
  5. 这是我创建的代码:

    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
    

    这可行,但似乎有点浪费(特别是对于大型数据帧)。有没有办法直接创建二进制表示列,而无需创建临时数据帧?

1 个答案:

答案 0 :(得分:1)

方法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)

jezrael的复制实验。

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)