使用GREP对数据进行子集

时间:2016-10-04 08:08:12

标签: bash awk grep subset

我有一个非常大的文本文件(16GB),我希望尽可能快地进行子集化。 以下是涉及的数据样本

0   M   4   0   
0   0   Q   0   10047345    3080290,4098689 50504886,4217515    9848058,1084315 50534229,4217515    50591618,4217515    26242582,2597528    34623075,3279130    68893581,5149883    50628761,4217517    32262001,3142702    35443881,3339757
0   108 C   0   50628761
0   1080    C   0   50628761
1   M   7   0
1   0   Q   0   17143989    
2   M   15  1   
2   0   Q   0   17143989    4219157,1841361,853923,1720163,1912374,1755325,4454730  65548702,4975721    197782,39086    54375043,4396765    31589696,3091097    6876504,851594  3374640,455375  13274885,1354902    31585771,3091016    61234218,4723345    31583582,3091014
2   27  C   0   31589696

每一行的第一个数字是 sessionID 以及任何一行' M'表示会话的开始(数据按会话分组)。 M后面的数字是 Day ,第二个数字是 userID ,用户可以有多个会话。

我想提取与特定用户相关的所有行,每个会话包括所有行,直到下一个' M'遇到行(可以是任意数量的行)。作为第二项任务,我还想提取与特定日期相关的所有会话行。

例如,对于上述数据,要提取用户ID' 0' 0的记录。输出将是:

0   M   4   0   
0   0   Q   0   10047345    3080290,4098689 50504886,4217515    9848058,1084315 50534229,4217515    50591618,4217515    26242582,2597528    34623075,3279130    68893581,5149883    50628761,4217517    32262001,3142702    35443881,3339757
0   108 C   0   50628761
0   1080    C   0   50628761
1   M   7   0
1   0   Q   0   17143989    

要提取第7天的记录,输出将是:

1   M   7   0
1   0   Q   0   17143989    

我相信到目前为止我已经取得了一些更加优雅和简单的解决方案,并且获得一些反馈和建议会很棒。谢谢。

我尝试了什么

我尝试使用 pcrgrep -M 直接应用此模式(匹配两个M'之间的数据),但很难在整个换行符中使用它。我仍然怀疑这可能是最快的选择,所以关于这是否可行的任何指导都会很棒。

下一部分非常分散,如果您已经有了更好解决方案的想法,则没有必要继续阅读!

如果不能解决上述问题,我将问题分为两部分:

  • 第1部分:隔离所有' M'行以获取属于该用户/日的会话列表

    1. grep方法很快(然后需要弄清楚如何使用这些数据)

      time grep -c "M\t.*\t$user_id" trainSample.txt >> sessions.txt

    2. 创建数组的awk方法很慢

      time myarr=$(awk '/M\t.*\t$user_id/ {print $1}' trainSample.txt

  • 第2部分:在第1部分中创建的列表中提取属于会话的所有行

    1. 继续使用awk方法,我为每个人运行grep,但这样做太慢了(完成16GB的天数)

      for i in "${!myarr[@]}"; 
      do 
        grep "^${myarr[$i]}\t" trainSample.txt >> sessions.txt
        echo -ne "Session $i\r"
      done
      
    2. 而不是像上面那样使用每个会话ID运行grep一次使用它们在一个grep命令中的速度要快得多(我用[1 | 2 | 3 | .. | 8]格式的8个sessionID运行它并且每个人分别花了相同的时间,即快了8倍)。但是,我需要弄清楚如何动态地执行此操作

更新

我实际上已经建立了一个工作解决方案,只需要几秒钟就可以完成,但它是一些凌乱且不灵活的bash coe,我还没有扩展到第二个(隔离天数)的情况。

1 个答案:

答案 0 :(得分:4)

  

我想提取与特定用户相关的所有行,每个会话包括所有行,直到遇到下一个“M”行(可以是任意数量的行)。

$ awk '$2=="M"{p=$4==0}p' file
0   M   4   0   
0   0   Q   0   10047345    3080290,4098689 50504886,4217515    9848058,1084315 50534229,4217515    50591618,4217515    26242582,2597528    34623075,3279130    68893581,5149883    50628761,4217517    32262001,3142702    35443881,3339757
0   108 C   0   50628761
0   1080    C   0   50628761
1   M   7   0
1   0   Q   0   17143989 
  

作为第二项任务,我还想提取与特定日期相关的所有会话行。

$ awk '$2=="M"{p=$3==7}p' file
1   M   7   0
1   0   Q   0   17143989