【问题标题】:Splitting string multiple times in DataFrame在DataFrame中多次拆分字符串
【发布时间】:2019-10-19 01:43:21
【问题描述】:

我在 DataFrame 中有一个列,其中包含一个字符串,我必须通过不同的分隔符从中检索两条信息:

ID  STR
280 11040402-38.58551%;11050101-9.29086%;11070101-52.12363%
351 11130203-35%;11130230-65%
510 11070103-69%
655 11090103-41.63463%;11160102-58.36537%
666 11130205-50.00%;11130207-50%

我一直在尝试使用本系列中的.apply 方法与 lambda 函数一起进行拆分,但无济于事:

df['STR'].apply(lambda x: y.split('-') for y in x.split(';'))

理想情况下,我不仅可以一次性拆分字符串,还可以将- 的左侧与右侧分开:

ID  STR.LEFT                       STR.RIGHT
280 [11040402, 11050101, 11070101] [38.58551%, 9.29086%, 52.12363%]
351 [11130203, 11130230]           [35%, 65%]
510 [11070103]                     [69%]
655 [11090103, 11160102]           [41.63463%, 58.36537%]
666 [11130205, 11130207]           [50.00%, 50%]

我相信这可以通过.apply 和切片来实现,但欢迎使用任何其他解决方案。

【问题讨论】:

    标签: python pandas list dataframe lambda


    【解决方案1】:

    你可以尝试多次拆分:

    # set ID as index
    df.set_index('ID', inplace=True)
    
    
    new_series = df.STR.str.split(';', expand=True).stack().reset_index(level=-1,drop=True)
    
    new_df = new_series.str.split('-', expand=True)
    
    new_df.groupby('ID').agg(list).reset_index()
    

    输出:

          ID  0                                     1
    --  ----  ------------------------------------  --------------------------------------
     0   280  ['11040402', '11050101', '11070101']  ['38.58551%', '9.29086%', '52.12363%']
     1   351  ['11130203', '11130230']              ['35%', '65%']
     2   510  ['11070103']                          ['69%']
     3   655  ['11090103', '11160102']              ['41.63463%', '58.36537%']
     4   666  ['11130205', '11130207']              ['50.00%', '50%']
    

    【讨论】:

    • 此解决方案不如@pirsquared 快,但它通过使用.stack 将DataFrame 分解为多行,为我提供了另一个技巧。谢谢
    【解决方案2】:

    str.split

    假设模式总是离开'l-r;l-r;l-r...'

    s = df.STR.str.split('-|;')
    df[['ID']].join(pd.concat({'STR.LEFT': s.str[::2], 'STR.RIGTH': s.str[1::2]}, axis=1))
    
        ID                        STR.LEFT                         STR.RIGTH
    0  280  [11040402, 11050101, 11070101]  [38.58551%, 9.29086%, 52.12363%]
    1  351            [11130203, 11130230]                        [35%, 65%]
    2  510                      [11070103]                             [69%]
    3  655            [11090103, 11160102]            [41.63463%, 58.36537%]
    4  666            [11130205, 11130207]                     [50.00%, 50%]
    

    如果你想将这些列表分解成单独的行

    s = df.STR.str.split('-|;')
    i = np.arange(len(df)).repeat(s.str.len() // 2)
    d = {'STR.LEFT': np.concatenate(s.str[::2]),
         'STR.RIGHT': np.concatenate(s.str[1::2])}
    df[['ID']].iloc[i].assign(**d).reset_index(drop=True)
    
        ID  STR.LEFT  STR.RIGHT
    0  280  11040402  38.58551%
    1  280  11050101   9.29086%
    2  280  11070101  52.12363%
    3  351  11130203        35%
    4  351  11130230        65%
    5  510  11070103        69%
    6  655  11090103  41.63463%
    7  655  11160102  58.36537%
    8  666  11130205     50.00%
    9  666  11130207        50%
    

    【讨论】:

    • 这显然是最快的解决方案,至少比其他解决方案快六倍。我真的很喜欢用两行代码构建它的优雅。但是,事实证明我还需要将 DataFrame 拆分为多行(在这种情况下重复 ID),我可以通过使用 @quang-hoang 解决方案而不用他的最后一行代码来实现。不过还不错!谢谢!
    • @mepqfilho 这就是为什么在您的问题中指定您的需求很重要。如果您接受的答案与您提出的问题相符,这对未来的读者会更有帮助。不管怎样,这没什么大不了的。我已经包含了一些额外的代码,这些代码将值放入单独的行中。希望对您有所帮助。
    【解决方案3】:

    一个str.extractall 调用就足以将这些对提取到单独的列中。然后,您可以使用 groupby 将它们聚合到列表中。

    (df['STR'].str.extractall(r'(.*?)-(.*?)(?=;|$)')
              .groupby(level=0)
              .agg(list)
              .set_axis(['STR.LEFT', 'STR.RIGHT'], axis=1, inplace=False))
    
                               STR.LEFT                         STR.RIGHT
    0  [11040402, ;11050101, ;11070101]  [38.58551%, 9.29086%, 52.12363%]
    1  [11130203, ;11130230]             [35%, 65%]                      
    2  [11070103]                        [69%]                           
    3  [11090103, ;11160102]             [41.63463%, 58.36537%]          
    4  [11130205, ;11130207]             [50.00%, 50%]  
    

    要使用 ID 加入,只需使用:join

    (df['STR'].str.extractall(r'(.*?)-(.*?)(?=;|$)')
              .groupby(level=0)
              .agg(list)
              .set_axis(['STR.LEFT', 'STR.RIGHT'], axis=1, inplace=False)
              .join(df['ID'])
    
                               STR.LEFT                         STR.RIGHT   ID
    0  [11040402, ;11050101, ;11070101]  [38.58551%, 9.29086%, 52.12363%]  280
    1  [11130203, ;11130230]             [35%, 65%]                        351
    2  [11070103]                        [69%]                             510
    3  [11090103, ;11160102]             [41.63463%, 58.36537%]            655
    4  [11130205, ;11130207]             [50.00%, 50%]                     666
    

    【讨论】:

    • 这是一个可接受的解决方案,但.extractall 方法似乎将; 作为字符串的一部分,最好不要它。
    猜你喜欢
    • 1970-01-01
    • 2015-12-02
    • 2022-12-29
    • 2022-01-15
    • 1970-01-01
    • 2020-11-10
    • 2017-01-07
    相关资源
    最近更新 更多