Python 创建数据帧中是否存在字符串的二进制表示形式
我有一个Pandas数据框,它由几列组成,其中的单元格可能包含字符串,也可能不包含字符串。例如:Python 创建数据帧中是否存在字符串的二进制表示形式,python,python-3.x,pandas,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','','','
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
我能想到的唯一方法是:
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
这是可行的,但似乎有点浪费(特别是对于大型数据帧)。有没有一种方法可以直接创建二进制表示列,而无需创建临时数据帧?首先检查空字符串或转换为
bool
,然后转换为int
,str
和最后一个连接
或sum
:
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会将dataframe设置为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')
然后调用tostring
和decode
。删除最后一个“\n”,然后用“\n”拆分
方法3(使用视图
参考:)
时间:
耶斯雷尔的重复实验
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)
非常感谢你的回答。非常有用,特别是建议方法的不同时间。非常感谢您的解决方案。您和@jezreal的回答都非常有用。我选择了jezreal的答案作为接受答案,因为时间安排显示了不同的组件如何进行比较,但我对你的答案投了赞成票,并且它为问题提供了一个有效的答案。刚刚注意到我的打字错误:@jezrael。对不起,我编辑了我的答案并获得了更多的加速。如果你需要的话,让你知道。谢谢你为回答这个问题所做的所有工作。我总是忘了看numpy中可用的选项,但这个答案表明它可以带来一些非常实时和高效的好处。非常酷。@user1718097没问题。有趣的问题:PBut刚刚注意到输出是01011而不是10100;我认为.where()方法应该是np.where(df!=“1”、“0”)而不是:-)
(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)