【问题标题】:How to extract an arithmetic operation from a string with Pandas如何使用 Pandas 从字符串中提取算术运算
【发布时间】:2020-09-30 21:24:50
【问题描述】:

在 Pandas 数据帧中

>> df.head()

    A                               B             C
0   1    â#0.00 + "s=?0.07 + 'due0.93    rt@-[ 3.01
1   2    â#0.02 + "s=?0.16 + 'due0.82    rt@-[ 2.97
...

我想只提取数值。专栏C我可以用,例如,

>> extr = df['C'].str.extract(r'(\d+\.\d+)', expand=False)
>> df['C'] = pd.to_numeric(extr)
>> df.head()

    A                               B       C
0   1    â#0.00 + "s=?0.07 + 'due0.93    3.01
1   2    â#0.02 + "s=?0.16 + 'due0.82    2.97
...

B 列有问题。如何提取 + 操作以及浮点数?我试过了

>> extr = df['B'].str.extract(r'(\d+\.\d+)\+(\d+\.\d+)\+(\d+\.\d+)', expand=False)

我希望能给我类似的东西

                    0
0    '0.00+0.07+0.93'
1    '0.02+0.16+0.82'
...

但它给了我三列,其中包含NaN 值:

       0      1      2
0    NaN    NaN    NaN
1    NaN    NaN    NaN
...

那么我怎样才能提取整个算术运算呢?

(只需要+操作,其他字符如-可以忽略。)

【问题讨论】:

    标签: python-3.x regex pandas


    【解决方案1】:

    一种方法是在提取的数据上运行str join,使用+作为分隔符

    import re
    
    df = pd.read_clipboard(sep='\s{2,}')
    
    
    df['extract'] = ["+".join(re.findall("(\d+\.?\d+)",entry)) for entry in df.B]
    
    
        A                 B                  C         extract
    0   1   â#0.00 + "s=?0.07 + 'due0.93    3.01    0.00+0.07+0.93
    1   2   â#0.02 + "s=?0.16 + 'due0.82    2.97    0.02+0.16+0.82
    

    【讨论】:

      【解决方案2】:

      Python 不是我的强项,但我会改用replace 并对两列进行操作,也许看看:

      df[['B', 'C']] = df[['B','C']].replace(r'[^\d.+]', '', regex=True)
      print(df)
      

      结果:

         A               B     C
      0  1  0.00+0.07+0.93  3.01
      1  2  0.02+0.16+0.82  2.97
      

      如果它只是 B 列,那么您可能只是使用:

      extr = df['B'].str.replace(r'[^\d.+]', '')
      

      【讨论】:

        【解决方案3】:

        另一种使用Series.str.findall的方法:

        df['B'] = df['B'].str.findall(r'(\d+(?:.\d+)?)').agg('+'.join)
        

        # print(df)
           A               B     C
        0  1  0.00+0.07+0.93  3.01
        1  2  0.02+0.16+0.82  2.97
        

        timeit所有解决方案的比较:

        df.shape
        (20000, 4)
        
        %%timeit -n100 @Shubham solution
        df['B'].str.findall(r'(\d+(?:.\d+)?)').agg('+'.join)
        31.9 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
        
        
        %%timeit -n100 @Rakesh solution
        df["B"].str.findall(r"(\d+\.\d+)").str.join("+")
        32.7 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
        
        
        %%timeit -n100 @Sammy solution
        ["+".join(re.findall("(\d+\.?\d+)",entry)) for entry in df.B]
        36.8 ms ± 431 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        
        
        %%timeit -n100 @JudV solution
        df['B'].str.replace(r'[^\d.+]', '')
        59.7 ms ± 5.81 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
        

        【讨论】:

        • @Judv 我想应该没有任何区别,但我会运行timeit 以确保并让您知道结果。
        • @Judv 得到了timeit 结果,猜猜它们在性能方面几乎相同。
        • @JudV 一栏B
        • 酷,似乎replacefindall 花费的时间是findall 的两倍。太糟糕了
        • 这让我认为 Pandas 中的字符串处理比 vanilla python 慢。至少对于这个用例来说——爱因斯坦的概括是存在的!
        【解决方案4】:

        这是使用str.findall.str.join("+") 的一种方法

        例如:

        df = pd.DataFrame({"B": ["""â#0.00 + "s=?0.07 + 'due0.93""", """â#0.02 + "s=?0.16 + 'due0.82"""]})
        df["Z"] = df["B"].str.findall(r"(\d+\.\d+)").str.join("+")
        
        print(df)
        

        输出:

                                      B               Z
        0  â#0.00 + "s=?0.07 + 'due0.93  0.00+0.07+0.93
        1  â#0.02 + "s=?0.16 + 'due0.82  0.02+0.16+0.82
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-08
          • 1970-01-01
          • 2020-03-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多