【问题标题】:Split row into multiple columns choosing just certain values from a row and dropping the rest将行拆分为多列,仅从一行中选择某些值并删除其余的
【发布时间】:2018-05-10 00:56:21
【问题描述】:

我有一个看起来像这样的 df:

name      surname     device
anna      smith       Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304
petr      lebowski    Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:48.0) Gecko/20100101 Firefox/48.0
eveline   jones       Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G903F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36

我的目标是将这些行拆分为多列,并仅选择某些值来创建新列,以便新的数据框如下所示:

name      surname     device        column1     column2               column3
anna      smith       as in df      iPhone CPU  iPhone OS 10_3_1      Mac OS X 
petr      lebowski    as in df      Macintosh   Intel Mac OS X 10.6   rv:48.0
eveline   jones       as in df      Linux       Android 6.0.1         SAMSUNG SM-G903F Build/MMB29K

所以基本上我只想从这些行中选择某些值:只需要括号中的值,用 ';' 分隔。

我知道如何逐个符号地拆分它:

def split_it(string):
    return re.findall(r"[\w']+|[.,!?;]", string)

以及如何创建具有拆分行的新列:

df['device'].str[1:-1].str.split(',', expand=True)

但是不知道如何只选择某些值并使用它们创建列...

【问题讨论】:

  • 你的例子不太行;第一行括号之间只有一个;

标签: python regex python-3.x pandas dataframe


【解决方案1】:

你需要一个更好的正则表达式:

pattern = re.compile(r"\((.+?); (.+?)(?: like|;) (.+?)\)")
new_columns = df.device.str.extract(pattern, expand=False)

您甚至可以为它们命名:

new_columns = df.Device.str.extract(r"\((?P<column1>.+?); (?P<column2>.+?)(?: like|;) (?P<column3>.+?)\)", expand=False)

然后,您只需将其余数据发送回 join 即可:

df = df.join(new_columns)

【讨论】:

    【解决方案2】:

    您可以利用较新的 regex module 和一些编程逻辑:

    import pandas as pd, regex as re
    
    # your df here
    
    rx = re.compile(r'(?:\G(?!\A)|^[^()]*\()([^();]+);?')
    
    def getter(col):
        # default empty dict
        empty = {'column1': '', 'column2': '', 'column3': ''}
    
        # generate results with a comprehension
        matches = {'column{}'.format(idx): val 
            for idx, val in enumerate(rx.findall(col), 1)}
        return pd.Series(dict(empty, **matches))
    
    df[["column1", "column2", "column3"]] = df["device"].apply(getter)
    print(df[["column1", "column2", "column3"]])
    

    这将为您的示例产生

         column1                              column2  \
    0     iPhone   CPU iPhone OS 10_3_1 like Mac OS X   
    1  Macintosh                  Intel Mac OS X 10.6   
    2      Linux                        Android 6.0.1   
    
                              column3  
    0                                  
    1                         rv:48.0  
    2   SAMSUNG SM-G903F Build/MMB29K  
    


    说明

    虽然肯定不完美,但我们的想法是在 () 中匹配 ;。这里需要\G,它与上次匹配停止的位置相匹配:

    (?:           # non-capturing group
        \G(?!\A)  # match where the last match stopped, not the beginning
        |         # or
        ^[^()]*\( # find the first (
    )
    (             # capture group 1
        [^();]+   # anything not (, ) or ; at least once
    )
    ;?            # semicolon optionally
    

    之后,getter 函数返回一个新的pd.Series(),它将保存在df 中。 getter() 内部的逻辑覆盖了一个空字典。

    a demo on regex101.com

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-21
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多