如何从Pandas数据帧(Python)中的字典中删除花括号,撇号和方括号

时间:2016-10-08 03:04:57

标签: python regex pandas

我在csv文件中有以下数据:

from StringIO import StringIO
import pandas as pd

the_data = """
ABC,2016-6-9 0:00,95,{'//PurpleCar': [115L], '//YellowCar': [403L], '//BlueCar': [16L], '//WhiteCar-XYZ': [0L]}
ABC,2016-6-10 0:00,0,{'//PurpleCar': [219L], '//YellowCar': [381L], '//BlueCar': [90L], '//WhiteCar-XYZ': [0L]}
ABC,2016-6-11 0:00,0,{'//PurpleCar': [817L], '//YellowCar': [21L], '//BlueCar': [31L], '//WhiteCar-XYZ': [0L]}
ABC,2016-6-12 0:00,0,{'//PurpleCar': [80L], '//YellowCar': [2011L], '//BlueCar': [8888L], '//WhiteCar-XYZ': [0L]}
ABC,2016-6-13 0:00,0,{'//PurpleCar': [32L], '//YellowCar': [15L], '//BlueCar': [4L], '//WhiteCar-XYZ': [0L]}
DEF,2016-6-16 0:00,0,{'//PurpleCar': [32L], '//BlackCar': [15L], '//PinkCar': [4L], '//NPO-GreenCar': [0L]}
DEF,2016-6-17 0:00,0,{'//PurpleCar': [32L], '//BlackCar': [15L], '//PinkCar': [4L], '//NPO-GreenCar': [0L]}
DEF,2016-6-18 0:00,0,{'//PurpleCar': [32L], '//BlackCar': [15L], '//PinkCar': [4L], '//NPO-GreenCar': [0L]}
DEF,2016-6-19 0:00,0,{'//PurpleCar': [32L], '//BlackCar': [15L], '//PinkCar': [4L], '//NPO-GreenCar': [0L]}
DEF,2016-6-20 0:00,0,{'//PurpleCar': [32L], '//BlackCar': [15L], '//PinkCar': [4L], '//NPO-GreenCar': [0L]}
"""

我将文件读入Pandas数据框,如下所示:

df = pd.read_csv(StringIO(the_data), sep=',')

然后,我添加了几个列标题,如下所示:

df.columns = ['Company',
                    'Date',
                    'Volume',
                    'Car1',
                    'Car2',
                    'Car3',
                    'Car4']

我看到数据如下:

ABC,2016-6-9 0:00,95,{'//PurpleCar': [115L], '//YellowCar': [403L], '//BlueCar': [16L], '//WhiteCar-XYZ': [0L]

但是,我希望看到数据没有以下任何一项:

a)开头的花括号("{")和字典末尾的花括号("}"

b)" L"在数值之后

c)围绕数值的方括号("[""]"

d)围绕钥匙的撇号

理想情况下,数据将按如下方式转换:

ABC,2016-6-9 0:00,95,//PurpleCar: 115, //YellowCar: 403, //BlueCar: 16, //WhiteCar-XYZ: 0

我试过了:

df['Car1'] = df['Car1'].str.strip(['{', '}', '[', 'L]'])

但是,它不起作用。它导致了Car1'列成为NaN值。

是否可以转换数据帧,使得数据帧的每一行读取如下?

ABC,2016-6-9 0:00,95,//PurpleCar: 115, //YellowCar: 403, //BlueCar: 16, //WhiteCar-XYZ: 0

谢谢!

更新

使用以下正则表达式:

df['Car1'] = df['Car1'].str.replace(r'\D+', '').astype('int')

结果如下:

ABC,2016-6-9 0:00,95, 115 , //YellowCar: 403, //BlueCar: 16, //WhiteCar-XYZ: 0

我们失去了' // PurpleCar'只留下115的数值。这是一个好的开始,但如果我们能看到' // PurpleCar'那就太棒了。也是关键。

有什么想法吗?

更新2:

基于piRSquared和HYRY的评论,我的目标是能够绘制数值结果。所以,我想让数据框看起来如下:

   Company   Date            PurpleCar  YellowCar   BlueCar     WhiteCar      

0  ABC       2016-6-9 0:00   115        403         16          0
1  ABC       2016-6-10 0:00  219        381         90          0
2  ABC       2016-6-11 0:00  817        21          31          0
3  ABC       2016-6-12 0:00  80         2011        8888        0
4  ABC       2016-6-13 0:00  32         15          4           0
5  DEF       2016-6-16 0:00  32         15          4           0
6  DEF       2016-6-17 0:00  32         15          4           0
7  DEF       2016-6-18 0:00  32         15          4           0
8  DEF       2016-6-19 0:00  32         15          4           0
9  DEF       2016-6-20 0:00  32         15          4           0

*更新3:*

最初发布的数据有一个小错误。这是数据:

the_data = """
ABC,2016-6-9 0:00,95,"{'//Purple': [115L], '//Yellow': [403L], '//Blue': [16L], '//White-XYZ': [0L]}"
ABC,2016-6-10 0:00,0,"{'//Purple': [219L], '//Yellow': [381L], '//Blue': [90L], '//White-XYZ': [0L]}"
ABC,2016-6-11 0:00,0,"{'//Purple': [817L], '//Yellow': [21L], '//Blue': [31L], '//White-XYZ': [0L]}"
ABC,2016-6-12 0:00,0,"{'//Purple': [80L], '//Yellow': [2011L], '//Blue': [8888L], '//White-XYZ': [0L]}"
ABC,2016-6-13 0:00,0,"{'//Purple': [32L], '//Yellow': [15L], '//Blue': [4L], '//White-XYZ': [0L]}"
DEF,2016-6-16 0:00,0,"{'//Purple': [32L], '//Black': [15L], '//Pink': [4L], '//NPO-Green': [3L]}"
DEF,2016-6-17 0:00,0,"{'//Purple': [32L], '//Black': [15L], '//Pink': [4L], '//NPO-Green': [0L]}"
DEF,2016-6-18 0:00,0,"{'//Purple': [32L], '//Black': [15L], '//Pink': [4L], '//NPO-Green': [7L]}"
DEF,2016-6-19 0:00,0,"{'//Purple': [32L], '//Black': [15L], '//Pink': [4L], '//NPO-Green': [14L]}"
DEF,2016-6-20 0:00,0,"{'//Purple': [32L], '//Black': [15L], '//Pink': [4L], '//NPO-Green': [21L]}"
"""

此数据与原始数据之间的差异是在开始大括号(("))之前和结束大括号("{"之后)的撇号"}"

3 个答案:

答案 0 :(得分:1)

修改:该文件似乎实际上是转发的CSV,因此我们不需要为此部分进行自定义解析。

正如@Blckknght在评论中指出的那样,该文件不是有效的CSV。我会在答案中做出一些假设。它们是

    <击>
  1. 您无法控制数据,因此无法正常使用逗号。
  2. 前三列不包含任何逗号。
  3. 第三列遵循python dict的语法。
  4. 列表中始终有一个值在dict值中。
  5. 首先,一些进口

    import ast
    import pandas as pd
    

    我们只是用逗号分隔行,因为我们不需要处理任何类型的CSV转义(假设#1和#2)。

    <击>
    rows = (line.split(",", 3) for line in the_data.splitlines() if line.strip() != "")
    
    fixed_columns = pd.DataFrame.from_records(rows, columns=["Company", "Date", "Value", "Cars_str"])
    

    <击>

    fixed_columns = pd.read_csv(..., names=["Company", "Date", "Value", "Cars_str"])
    

    前三列是固定的,我们将它们保留原样。我们可以使用ast.literal_eval解析的最后一列,因为它是dict(假设#3)。如果格式改变比正则表达式,这是IMO更可读和更灵活。您也可以提前检测格式更改。

    cars = fixed_columns["Cars_str"].apply(ast.literal_eval)
    del fixed_columns["Cars_str"]
    

    这部分回答your other question

    我们准备函数来处理dict的键和值,这样如果我们对dict内容的假设失败,它们就会失败。

    def get_single_item(list_that_always_has_single_item):
        v, = list_that_always_has_single_item
        return v
    
    def extract_car_name(car_str):
        assert car_str.startswith("//"), car_str
        return car_str[2:]
    

    我们应用函数并构造pd.Series,这使我们能够......

    dynamic_columns = cars.apply(
        lambda x: pd.Series({
                extract_car_name(k): get_single_item(v) 
                for k, v in x.items()
        }))    
    

    ...将列添加到数据框

    result = pd.concat([fixed_columns, dynamic_columns], axis=1)
    result
    

    最后,我们得到了表格:

      Company            Date Value  BlackCar  BlueCar  NPO-GreenCar  PinkCar  \
    0     ABC   2016-6-9 0:00    95       NaN     16.0           NaN      NaN   
    1     ABC  2016-6-10 0:00     0       NaN     90.0           NaN      NaN   
    2     ABC  2016-6-11 0:00     0       NaN     31.0           NaN      NaN   
    3     ABC  2016-6-12 0:00     0       NaN   8888.0           NaN      NaN   
    4     ABC  2016-6-13 0:00     0       NaN      4.0           NaN      NaN   
    5     DEF  2016-6-16 0:00     0      15.0      NaN           0.0      4.0   
    6     DEF  2016-6-17 0:00     0      15.0      NaN           0.0      4.0   
    7     DEF  2016-6-18 0:00     0      15.0      NaN           0.0      4.0   
    8     DEF  2016-6-19 0:00     0      15.0      NaN           0.0      4.0   
    9     DEF  2016-6-20 0:00     0      15.0      NaN           0.0      4.0   
    
       PurpleCar  WhiteCar-XYZ  YellowCar  
    0      115.0           0.0      403.0  
    1      219.0           0.0      381.0  
    2      817.0           0.0       21.0  
    3       80.0           0.0     2011.0  
    4       32.0           0.0       15.0  
    5       32.0           NaN        NaN  
    6       32.0           NaN        NaN  
    7       32.0           NaN        NaN  
    8       32.0           NaN        NaN  
    9       32.0           NaN        NaN  
    

答案 1 :(得分:0)

这应该可以解决问题

F# Interactive for F# 3.1 (Open Source Edition)

enter image description here

答案 2 :(得分:0)

我认为最好将字符串转换为两列:

from io import StringIO
import pandas as pd


df = pd.read_csv(StringIO(the_data), sep=',', header=None)
df.columns = ['Company','Date','Volume','Car1','Car2','Car3','Car4']

cars = ["Car1", "Car2", "Car3", "Car4"]
pattern = r"//(?P<color>.+?)':.*?(?P<value>\d+)"
df2 = pd.concat([df[col].str
                    .extract(pattern)
                    .assign(value=lambda self: pd.to_numeric(self["value"]))
                    for col in cars],
                axis=1, keys=cars)

结果:

        Car1             Car2           Car3                Car4      
       color value      color value    color value         color value
0  PurpleCar   115  YellowCar   403  BlueCar    16  WhiteCar-XYZ     0
1  PurpleCar   219  YellowCar   381  BlueCar    90  WhiteCar-XYZ     0
2  PurpleCar   817  YellowCar    21  BlueCar    31  WhiteCar-XYZ     0
3  PurpleCar    80  YellowCar  2011  BlueCar  8888  WhiteCar-XYZ     0
4  PurpleCar    32  YellowCar    15  BlueCar     4  WhiteCar-XYZ     0
5  PurpleCar    32   BlackCar    15  PinkCar     4  NPO-GreenCar     0
6  PurpleCar    32   BlackCar    15  PinkCar     4  NPO-GreenCar     0
7  PurpleCar    32   BlackCar    15  PinkCar     4  NPO-GreenCar     0
8  PurpleCar    32   BlackCar    15  PinkCar     4  NPO-GreenCar     0
9  PurpleCar    32   BlackCar    15  PinkCar     4  NPO-GreenCar     0