解包位后将列连接到数据帧

时间:2021-06-30 14:19:33

标签: python pandas dataframe concatenation multiple-columns

我有一个包含 3 列和以下值的数据框

df = pd.DataFrame(columns=['a', 'b', 'c'])
df.loc[0] = [4, 6, 8]
df.loc[1] = [5, 9, 7]
df.loc[2] = [8, 2, 1]

生成的数据框将如下所示:

    a  b  c

0   4  6  8
1   5  9  7
2   8  2  1

我想将每行中的前两个数字(或列)转换为它们相应的 8 位二进制值,并在 df 中替换它们。但保留最后一列 df['c'] 原样。

例如df.loc[0]应该转换为

    df.loc[0] = [0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,8]

这里,在 df.loc[0] 中,前八个 0 和 1 等价于 4,接下来的八个等价于 6。但最后一个数字保留在 int 中。

这是我正在做的:

    # save df.iloc[0, 'c'] in a different dataframe
    df_1.iloc[0, 'c'] = df.iloc[0, 'c']
    df.drop(columns='c', inplace=True)
    each_row = np.array(df.iloc[0, 'a']) # read each row
    each_row = np.array(each_row, dtype=np.uint8) #convert them to uint8 type
    each_row = np.unpackbits(each_row)
    each_row = pd.Series(each_row.astype(int)) #convert uint8 back to a series to concatenate to a dataframe

现在我想插入列 a、b,然后复制回 c。

如何用变量 each_row 中的值替换 df.loc[0] 中的 4 和 6?有没有一种有效的方法可以对数据帧的所有行执行此操作,而无需在 for 循环中运行它们?

3 个答案:

答案 0 :(得分:1)

您可以尝试一些看起来令人困惑的字典理解。

d = {col: np.append(np.concatenate(df.loc[:1, col].apply(lambda x: list(f'{x:08b}'))), df.loc[2, col]) for col in df.columns}
new_df = pd.DataFrame(d)

    a  b  c
0   0  0  0
1   0  0  0
2   0  0  0
3   0  0  0
4   0  0  1
5   1  1  0
6   0  0  0
7   0  1  0
8   0  0  0
9   0  0  0
10  0  0  0
11  0  0  0
12  0  1  0
13  1  0  0
14  1  0  1
15  0  1  0
16  8  7  1

答案 1 :(得分:1)

虽然您的问题有点令人困惑,但从您提供的值来看,我想您确实想将 [4, 6, 8] 映射到 [0,0,0,0,0,1,0,0, 0,0,0,0,0,1,1,0,8]。 您应该编辑您的问题以获得更详细的答案。

以下是为实现这一目标而逐步构建的不同功能。

输出一系列二进制表示:

def my_format(s):
    return format(s['a'], '08b')+format(s['b'], '08b')+str(s['c'])
df.apply(my_format, axis=1)

输出:

0    00000100000001018
1    00000110000010012
2    00001000000001111

输出一系列列表:

def my_format2(s):
    return list(map(int, ''.join(map('{:08b}'.format, s[:2]))))+[s[2]]
df.apply(my_format2, axis=1)

输出:

0    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, ...
1    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, ...
2    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, ...

输出数据帧

def my_format3(s):
    return pd.Series(list(map(int, ''.join(map('{:08b}'.format, s[:2]))))+[s[2]])
df.apply(my_format3, axis=1)

输出:

    0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
0   0   0   0   0   0   1   0   0   0   0   0   0   0   1   1   0   8
1   0   0   0   0   0   1   0   0   0   0   0   0   0   1   1   0   8
2   0   0   0   0   0   1   0   0   0   0   0   0   0   1   1   0   8

编辑:另一种可能性

df = pd.DataFrame([[4,6,8],[4,9,7],[8,2,1]], columns=['a', 'b', 'c'])
df[['a', 'b']] = df[['a', 'b']].applymap('{:08b}'.format)
df

输出:(注意,如果你想保留前导零,a和b是字符串)

          a         b  c
0  00000100  00000110  8
1  00000100  00001001  7
2  00001000  00000010  1

答案 2 :(得分:0)

你可以试试:

首先创建一个掩码:

mask=df.index%3>=2

那么:

out=df[~mask].applymap(lambda x:format(x, "08b")).append(df[mask]).sort_index()
out=out.T.astype(str).agg(lambda x:list(''.join(x)),1)

现在,如果您打印 out,您将获得预期的输出

另外:

列表中的值是字符串类型如果你想要 int 那么:

out=out.map(lambda x:[int(y) for y in x])