【问题标题】:Changing cell values in a row conditioned on values of another row根据另一行的值更改一行中的单元格值
【发布时间】:2020-05-23 02:35:38
【问题描述】:

我正在尝试更改包含数千行的数据框,每行看起来都像以下变体之一:

table, th, td {
  border: 1px solid black
}
<table>
  <tr>
    <th> a </th>
    <th> b </th>
    <th> c </th>
  </tr>
  <tr>
    <td> $10 x and $7 y </td>
    <td> NA </td>
    <td> NA </td>
  </tr>
  <tr>
    <td> $70 a; $95 b </td>
    <td> NA </td>
    <td> NA </td>
  </tr>
  <tr>
    <td> $6 j </td>
    <td> NA </td>
    <td> NA </td>
  </tr>
</table>

并将其更改为:

table, th, td {
  border: 1px solid black
}
    <table>
      <tr>
        <th> a </th>
        <th> b </th>
        <th> c </th>
      </tr>
      <tr>
        <td> $10 x and $7 y </td>
        <td> $10 x </td>
        <td> $7 y </td>
      </tr>
      <tr>
        <td> $70 a; $95 b </td>
        <td> $70 a </td>
        <td> $95 b </td>
      </tr>
      <tr>
        <td> $6 j </td>
        <td> $6 j </td>
        <td> NA </td>
      </tr>
    </table>

这是我当前的代码来实现这一点(我使用美元符号的数量,因为这是确定交易数量的唯一一致值):

(这是格式化为 data.table,以防万一)

  df$b[(str_count(df$a, pattern = "\\$") == 2)] = unlist(strsplit(df$a, " and "))[1]
  df$c[(str_count(df$a, pattern = "\\$") == 2)] = unlist(strsplit(df$a, " and "))[2]
  df$b[str_count(df$a, pattern = "\\$") < 2] = df$a 

现在,我得到的不是预期的结果:

table, th, td {
  border: 1px solid black
}
<table>
  <tr>
    <th> a </th>
    <th> b </th>
    <th> c </th>
  </tr>
  <tr>
    <td> $10 x and $7 y </td>
    <td> $10 x </td>
    <td> $7 y </td>
  </tr>
  <tr>
    <td> $70 a; $95 b </td>
    <td> $10 x</td>
    <td> $7 y</td>
  </tr>
  <tr>
    <td> $6 j </td>
    <td> $6 j </td>
    <td> NA </td>
  </tr>
</table>

有谁知道如何解决这个问题?我认为这与strsplit() 将第一个子集行并将其应用于子集中的每一行这一事实有关,但我不知道如何更改它以使其正常工作。

【问题讨论】:

    标签: r data.table strsplit


    【解决方案1】:

    不要尝试编写代码来解析 HTML,只需调用 HTML 解析器:

    library(rvest)
    library(tidyverse)
    
    stage1 <- 
      "<table>
      <tr>
        <th> a </th>
        <th> b </th>
        <th> c </th>
      </tr>
      <tr>
        <td> $10 x and $7 y </td>
        <td> NA </td>
        <td> NA </td>
      </tr>
      <tr>
        <td> $70 a; $95 b </td>
        <td> NA </td>
        <td> NA </td>
      </tr>
      <tr>
        <td> $6 j </td>
        <td> NA </td>
        <td> NA </td>
      </tr>
    </table>" %>% 
      rvest::minimal_html() %>% 
      rvest::html_node("table") %>% 
      rvest::html_table() %>% 
      as_tibble()
    
    stage1
    
    # A tibble: 3 x 3
      a              b     c    
      <chr>          <lgl> <lgl>
    1 $10 x and $7 y NA    NA   
    2 $70 a; $95 b   NA    NA   
    3 $6 j           NA    NA   
    

    现在使用separate 和正则表达式清理stage1

    stage1 %>% 
      select(a) %>% 
      separate(col = "a", into = c("b", "c"), 
               sep = "(?ix) \\s* (and|;) \\s*",   # Perl stye regex, cases insensitive.
               remove = FALSE, 
               fill= "right")
    
    
      a              b     c    
      <chr>          <chr> <chr>
    1 $10 x and $7 y $10 x $7 y 
    2 $70 a; $95 b   $70 a $95 b
    3 $6 j           $6 j  NA   
    

    【讨论】:

    • 抱歉回复晚了 - 感谢您的出色解决方案!我认为,我必须在正则表达式方面做得更好!
    • 很多关于正则表达式的好材料。在当前上下文中,请查看 r4ds.had.co.nz/… 尽管他没有涉及 Perl 样式或前瞻/后视。但它的可读性很好,练习值得练习。
    【解决方案2】:

    你可以从stringr使用str_split_fixed

    stringr::str_split_fixed(df$a, '\\s*(;|and)\\s*', 2)
    
    #       [,1]    [,2]   
    #[1,] "$10 x" "$7 y" 
    #[2,] "$70 a" "$95 b"
    #[3,] "$6 j"  ""     
    

    【讨论】:

      猜你喜欢
      • 2020-11-28
      • 2013-12-27
      • 1970-01-01
      • 2022-11-14
      • 2018-01-16
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 2018-06-22
      相关资源
      最近更新 更多