如何将数据框列拆分为多个列

时间:2013-08-03 15:27:42

标签: python csv pandas dataframe

经过多次激励,我开始将我的R脚本迁移到Python。我在R中的大多数工作都涉及数据框,我正在使用pandas包中的DataFrame对象。在我的脚本中,我需要读取csv文件并将数据导入DataFrame对象。接下来,我需要将十六进制值转换为标记为DATA的列为按位数据,然后创建16个新列,每列一个。

文件test.txt中的示例输入数据如下所示,

  

PREFIX,TEST,ZONE,ROW,COL,DATA

     

6_6,READ,0,0,0,BFED

     

6_6,READ,0,1,0,BB7D

     

6_6,READ,0,2,0,FFF7

     

6_6,READ,0,3,0,E7FF

     

6_6,READ,0,4,0,FBF8

     

6_6,READ,0,5,0,DE75

     

6_6,READ,0,6,0,DFFE

我的python脚本test.py如下,

import glob

import pandas as pd

import numpy as np

fname = 'test.txt'

df = pd.read_csv(fname, comment="#")

dfs = df[df.TEST == 'READ']

# function to convert the hexstring into a binary string

def hex2bin(hstr):

    return bin(int(hstr,16))[2:]


# convert the hexstring in column DATA to binarystring ROWDATA

dfs['BINDATA'] = dfs['DATA'].apply(hex2bin)

# get rid of the column DATA

del dfs['DATA']

当我运行此脚本并检查对象dfs时,我得到以下内容,

  

PREFIX TEST RONE ROW COL BINDATA

     

0 6_6 READ 0 0 0 1011111111101101

     

1 6_6 READ 0 1 0 1011101101111101

     

2 6_6 READ 0 2 0 1111111111110111

     

3 6_6 READ 0 3 0 1110011111111111

     

4 6_6 READ 0 4 0 1111101111111000

     

5 6_6 READ 0 5 0 1101111001110101

     

6 6_6阅读0 6 0 1101111111111110

     
    
      

    
  

所以现在我不确定如何将名为BINDATA的列拆分为16个新列(可以命名为B0,B0,B2,....,B15)。任何帮助将不胜感激。

谢谢&的问候,

Derric。

2 个答案:

答案 0 :(得分:4)

我不知道它是否可以更简单地完成(没有for循环),但这样做可以解决问题:

for i in range(16):
    dfs['B'+str(i)] = dfs['BINDATA'].str[i]

Series的str属性允许访问一些对每个元素起作用的矢量化字符串方法(参见docs:http://pandas.pydata.org/pandas-docs/stable/basics.html#vectorized-string-methods)。在这种情况下,我们只是索引字符串以访问不同的字符 这给了我:

In [20]: dfs
Out[20]:
            BINDATA B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15
0  1011111111101101  1  0  1  1  1  1  1  1  1  1   1   0   1   1   0   1
1  1011101101111101  1  0  1  1  1  0  1  1  0  1   1   1   1   1   0   1
2  1111111111110111  1  1  1  1  1  1  1  1  1  1   1   1   0   1   1   1
3  1110011111111111  1  1  1  0  0  1  1  1  1  1   1   1   1   1   1   1
4  1111101111111000  1  1  1  1  1  0  1  1  1  1   1   1   1   0   0   0
5  1101111001110101  1  1  0  1  1  1  1  0  0  1   1   1   0   1   0   1
6  1101111111111110  1  1  0  1  1  1  1  1  1  1   1   1   1   1   1   0

如果您希望将它们作为整数而不是字符串,则可以在for循环中添加.astype(int)


编辑:另一种方法(oneliner,但你必须在第二步更改列名):

In [34]: splitted = dfs['BINDATA'].apply(lambda x: pd.Series(list(x)))

In [35]: splitted.columns = ['B'+str(x) for x in splitted.columns]

In [36]: dfs.join(splitted)
Out[36]:
            BINDATA B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15
0  1011111111101101  1  0  1  1  1  1  1  1  1  1   1   0   1   1   0   1
1  1011101101111101  1  0  1  1  1  0  1  1  0  1   1   1   1   1   0   1
2  1111111111110111  1  1  1  1  1  1  1  1  1  1   1   1   0   1   1   1
3  1110011111111111  1  1  1  0  0  1  1  1  1  1   1   1   1   1   1   1
4  1111101111111000  1  1  1  1  1  0  1  1  1  1   1   1   1   0   0   0
5  1101111001110101  1  1  0  1  1  1  1  0  0  1   1   1   0   1   0   1
6  1101111111111110  1  1  0  1  1  1  1  1  1  1   1   1   1   1   1   0

答案 1 :(得分:1)

以下是如何在没有循环的情况下执行此操作(但实际上并非如此,因为此代码中存在大量隐式循环):

import pandas as pd

# read the above frame from the clipboard
df = pd.read_clipboard(converters={'BINDATA': str})
df = df.fillna(nan).replace('None', nan).dropna(axis=0, how='all')

# here are the lines that matter
bindata = df.BINDATA.apply(list).apply(Series)
bindata.columns = bindata.columns.map('B{0}'.format)
res = pd.concat([df, bindata], axis=1).convert_objects(convert_numeric=True)