Efficient grep using Python?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • sf

    #1

    Efficient grep using Python?

    Just started thinking about learning python.

    Is there any place where I can get some free examples, especially for
    following kind of problem ( it must be trivial for those using python)

    I have files A, and B each containing say 100,000 lines (each line=one
    string without any space)

    I want to do

    " A - (A intersection B) "

    Essentially, want to do efficient grep, i..e from A remove those lines which
    are also present in file B.


  • John Hunter

    #2
    Re: Efficient grep using Python?

    >>>>> "sf" == sf <sf@sf.sf> writes:

    sf> Just started thinking about learning python. Is there any
    sf> place where I can get some free examples, especially for
    sf> following kind of problem ( it must be trivial for those using
    sf> python)

    sf> I have files A, and B each containing say 100,000 lines (each
    sf> line=one string without any space)

    sf> I want to do

    sf> " A - (A intersection B) "

    sf> Essentially, want to do efficient grep, i..e from A remove
    sf> those lines which are also present in file B.

    If you're only talking about 100K lines or so, and you have a
    reasonably modern computer, you can do this all in memory. If order
    doesn't matter (it probably does) you can use a set to get all the
    lines in file B that are not in A

    from sets import Set
    A = Set(file('test1 .dat').readline s())
    B = Set(file('test2 .dat').readline s())
    print B-A

    To preserve order, you should use a dictionary that maps lines to line
    numbers. You can later use these numbers to sort

    A = dict([(line, num) for num,line in enumerate(file( 'test1.dat'))])
    B = dict([(line, num) for num,line in enumerate(file( 'test2.dat'))])

    keep = [(num, line) for line,num in B.items() if not A.has_key(line)]
    keep.sort()
    for num, line in keep:
    print line,

    Now someone else will come along and tell you all this functionality
    is already in the standard library. But it's always fun to hack this
    out yourself once because python makes such things so damned easy.

    JDH

    Comment

    • Fredrik Lundh

      #3
      Re: Efficient grep using Python?

      "sf" <sf@sf.sf> wrote:
      [color=blue]
      > I have files A, and B each containing say 100,000 lines (each line=one
      > string without any space)
      >
      > I want to do
      >
      > " A - (A intersection B) "
      >
      > Essentially, want to do efficient grep, i..e from A remove those lines which
      > are also present in file B.[/color]

      that's an unusual definition of "grep", but the following seems to
      do what you want:

      afile = "a.txt"
      bfile = "b.txt"

      bdict = dict.fromkeys(o pen(bfile).read lines())

      for line in open(afile):
      if line not in bdict:
      print line,

      </F>



      Comment

      • P@draigBrady.com

        #4
        Re: Efficient grep using Python?

        sf wrote:[color=blue]
        > Just started thinking about learning python.
        >
        > Is there any place where I can get some free examples, especially for
        > following kind of problem ( it must be trivial for those using python)
        >
        > I have files A, and B each containing say 100,000 lines (each line=one
        > string without any space)
        >
        > I want to do
        >
        > " A - (A intersection B) "
        >
        > Essentially, want to do efficient grep, i..e from A remove those lines which
        > are also present in file B.[/color]

        You could implement elegantly using the new sets feature
        For reference here is the unix way to do it:

        sort a b b | uniq -u

        --
        Pádraig Brady - http://www.pixelbeat.org
        --

        Comment

        • Tim Peters

          #5
          Re: Efficient grep using Python?

          ["sf" <sf@sf.sf>][color=blue][color=green]
          >> I have files A, and B each containing say 100,000 lines (each
          >> line=one string without any space)
          >>
          >> I want to do
          >>
          >> " A - (A intersection B) "
          >>
          >> Essentially, want to do efficient grep, i..e from A remove those
          >> lines which are also present in file B.[/color][/color]

          [Fredrik Lundh][color=blue]
          > that's an unusual definition of "grep", but the following seems to
          > do what you want:
          >
          > afile = "a.txt"
          > bfile = "b.txt"
          >
          > bdict = dict.fromkeys(o pen(bfile).read lines())
          >
          > for line in open(afile):
          > if line not in bdict:
          > print line,
          >
          > </F>[/color]

          Note that an open file is an iterable object, yielding the lines in
          the file. The "for" loop exploited that above, but fromkeys() can
          also exploit it. That is,

          bdict = dict.fromkeys(o pen(bfile))

          is good enough (there's no need for the .readlines()).

          Comment

          • Fredrik Lundh

            #6
            Re: Efficient grep using Python?

            Tim Peters wrote:
            [color=blue][color=green]
            >> bdict = dict.fromkeys(o pen(bfile).read lines())
            >>
            >> for line in open(afile):
            >> if line not in bdict:
            >> print line,
            >>
            >> </F>[/color]
            >
            > Note that an open file is an iterable object, yielding the lines in
            > the file. The "for" loop exploited that above, but fromkeys() can
            > also exploit it. That is,
            >
            > bdict = dict.fromkeys(o pen(bfile))
            >
            > is good enough (there's no need for the .readlines()).[/color]

            (sigh. my brain knows that, but my fingers keep forgetting)

            and yes, for this purpose, "dict.fromk eys" can be replaced
            with "set".

            bdict = set(open(bfile) )

            (and then you can save a few more bytes by renaming the
            variable...)

            </F>



            Comment

            • Tim Peters

              #7
              Re: Efficient grep using Python?

              [Fredrik Lundh][color=blue][color=green][color=darkred]
              >>> bdict = dict.fromkeys(o pen(bfile).read lines())
              >>>
              >>> for line in open(afile):
              >>> if line not in bdict:
              >>> print line,
              >>>
              >>> </F>[/color][/color][/color]

              [Tim Peters][color=blue][color=green]
              >> Note that an open file is an iterable object, yielding the lines in
              >> the file. The "for" loop exploited that above, but fromkeys() can
              >> also exploit it. That is,
              >>
              >> bdict = dict.fromkeys(o pen(bfile))
              >>
              >> is good enough (there's no need for the .readlines()).[/color][/color]

              [/F][color=blue]
              > (sigh. my brain knows that, but my fingers keep forgetting)
              >
              > and yes, for this purpose, "dict.fromk eys" can be replaced
              > with "set".
              >
              > bdict = set(open(bfile) )
              >
              > (and then you can save a few more bytes by renaming the
              > variable...)[/color]

              Except the latter two are just shallow spelling changes. Switching
              from fromkeys(open(f ).readlines()) to fromkeys(open(f )) is much more
              interesting, since it can allow major reduction in memory use. Even
              if all the lines in the file are pairwise distinct, not materializing
              them into a giant list can be a significant win. I wouldn't have
              bothered replying if the only point were that you can save a couple
              bytes of typing <wink>.

              Comment

              • P@draigBrady.com

                #8
                Re: Efficient grep using Python?

                Christos TZOTZIOY Georgiou wrote:[color=blue]
                > On Wed, 15 Dec 2004 16:10:08 +0000, rumours say that P@draigBrady.co m
                > might have written:
                >
                >[color=green][color=darkred]
                >>>Essentiall y, want to do efficient grep, i..e from A remove those lines which
                >>>are also present in file B.[/color]
                >>
                >>You could implement elegantly using the new sets feature
                >>For reference here is the unix way to do it:
                >>
                >>sort a b b | uniq -u[/color]
                >
                >
                > No, like I just wrote in another post, he wants
                >
                > $ grep -vf B A
                >
                > I think that
                >
                > $ sort A B B | uniq -u
                >
                > can be abbreviated to
                >
                > $ sort -u A B B
                >
                > which is the union rather than the intersection of the files[/color]

                wrong. Notice the -u option to uniq.

                [color=blue]
                > wastes some time by considering B twice[/color]

                I challenge you to a benchmark :-)
                [color=blue]
                > and finally destroys original line
                > order (should it be important).[/color]

                true

                --
                Pádraig Brady - http://www.pixelbeat.org
                --

                Comment

                • Christos TZOTZIOY Georgiou

                  #9
                  Re: Efficient grep using Python?

                  On Thu, 16 Dec 2004 14:28:21 +0000, rumours say that P@draigBrady.co m
                  might have written:

                  [sf][color=blue][color=green][color=darkred]
                  >>>>Essentially , want to do efficient grep, i..e from A remove those lines which
                  >>>>are also present in file B.[/color][/color][/color]

                  [p@draig][color=blue][color=green][color=darkred]
                  >>>You could implement elegantly using the new sets feature
                  >>>For reference here is the unix way to do it:
                  >>>
                  >>>sort a b b | uniq -u[/color][/color][/color]

                  [Christos][color=blue][color=green]
                  >> No, like I just wrote in another post, he wants
                  >>
                  >> $ grep -vf B A
                  >>
                  >> I think that
                  >>
                  >> $ sort A B B | uniq -u
                  >>
                  >> can be abbreviated to
                  >>
                  >> $ sort -u A B B
                  >>
                  >> which is the union rather than the intersection of the files[/color][/color]

                  [P@draig][color=blue]
                  >wrong. Notice the -u option to uniq.
                  >http://marc.free.net.ph/message/2002....1bc24964.html[/color]

                  I see your point. That's a new to me use of uniq, since I started using
                  Unices long before GNU versions of the tools, but then, I might have
                  missed the -u option.

                  $ cat A
                  aa
                  ss
                  dd
                  ff
                  gg
                  hh
                  jj
                  kk
                  $ cat B
                  aa
                  dd
                  gg
                  jj
                  pp
                  $ time sort A B B | uniq -u
                  ff
                  hh
                  kk
                  ss

                  real 0m0.004s
                  user 0m0.004s
                  sys 0m0.004s
                  $ time grep -vf B A
                  ss
                  ff
                  hh
                  kk

                  real 0m0.003s
                  user 0m0.000s
                  sys 0m0.003s

                  So I stand corrected that your solution does *not* give the union.
                  [color=blue][color=green]
                  >> wastes some time by considering B twice[/color]
                  >
                  >I challenge you to a benchmark :-)[/color]

                  Well, the numbers I provided above are almost meaningless with such a
                  small set (and they easily could be reverse, I just kept the
                  convenient-to-me first run :). Do you really believe that sorting three
                  files and then scanning their merged output counting duplicates is
                  faster than scanning two files (and doing lookups during the second
                  scan)?

                  $ python
                  Python 2.3.3 (#1, Aug 31 2004, 13:51:39)
                  [GCC 3.3.3 (SuSE Linux)] on linux2
                  Type "help", "copyright" , "credits" or "license" for more information.[color=blue][color=green][color=darkred]
                  >>> x=open('/usr/share/dict/words').readlin es()
                  >>> len(x)[/color][/color][/color]
                  45378[color=blue][color=green][color=darkred]
                  >>> import random
                  >>> random.shuffle( x)
                  >>> open("/tmp/A", "w").writelines (x)
                  >>> random.shuffle( x)
                  >>> open("/tmp/B", "w").writelines (x[:1000])
                  >>>[/color][/color][/color]
                  $ time sort A B B | uniq -u >/dev/null

                  real 0m0.311s
                  user 0m0.315s
                  sys 0m0.008s
                  $ time grep -Fvf B A >/dev/null

                  real 0m0.067s
                  user 0m0.064s
                  sys 0m0.003s

                  (Yes, I cheated by adding the F (for no regular expressions) flag :)
                  [color=blue][color=green]
                  >> and finally destroys original line
                  >> order (should it be important).[/color]
                  >
                  >true[/color]

                  That's our final agreement :)
                  --
                  TZOTZIOY, I speak England very best.
                  "Be strict when sending and tolerant when receiving." (from RFC1958)
                  I really should keep that in mind when talking with people, actually...

                  Comment

                  • P@draigBrady.com

                    #10
                    Re: Efficient grep using Python?

                    Christos TZOTZIOY Georgiou wrote:[color=blue]
                    > On Thu, 16 Dec 2004 14:28:21 +0000, rumours say that P@draigBrady.co m[color=green]
                    >>I challenge you to a benchmark :-)[/color]
                    >
                    >
                    > Well, the numbers I provided above are almost meaningless with such a
                    > small set (and they easily could be reverse, I just kept the
                    > convenient-to-me first run :). Do you really believe that sorting three
                    > files and then scanning their merged output counting duplicates is
                    > faster than scanning two files (and doing lookups during the second
                    > scan)?
                    >
                    > $ python
                    > Python 2.3.3 (#1, Aug 31 2004, 13:51:39)
                    > [GCC 3.3.3 (SuSE Linux)] on linux2
                    > Type "help", "copyright" , "credits" or "license" for more information.
                    >[color=green][color=darkred]
                    >>>>x=open('/usr/share/dict/words').readlin es()
                    >>>>len(x)[/color][/color]
                    >
                    > 45378
                    >[color=green][color=darkred]
                    >>>>import random
                    >>>>random.shuf fle(x)
                    >>>>open("/tmp/A", "w").writelines (x)
                    >>>>random.shuf fle(x)
                    >>>>open("/tmp/B", "w").writelines (x[:1000])
                    >>>>[/color][/color]
                    >
                    > $ time sort A B B | uniq -u >/dev/null
                    >
                    > real 0m0.311s
                    > user 0m0.315s
                    > sys 0m0.008s
                    > $ time grep -Fvf B A >/dev/null
                    >
                    > real 0m0.067s
                    > user 0m0.064s
                    > sys 0m0.003s
                    >
                    > (Yes, I cheated by adding the F (for no regular expressions) flag :)[/color]

                    Also you only have 1000 entries in B!
                    Try it again with all entries in B also ;-)
                    Remember the original poster had 100K entries!
                    [color=blue][color=green][color=darkred]
                    >>>and finally destroys original line
                    >>>order (should it be important).[/color]
                    >>
                    >>true[/color]
                    >
                    > That's our final agreement :)[/color]

                    Note the order is trivial to restore with a
                    "decorate-sort-undecorate" idiom.

                    --
                    Pádraig Brady - http://www.pixelbeat.org
                    --

                    Comment

                    • sf

                      #11
                      Re: Efficient grep using Python?

                      The point is that when you have 100,000s of records, this grep becomes
                      really slow?

                      Any comments?

                      Thats why I looked for python :)

                      [color=blue]
                      > that would be
                      >
                      > grep -vf B A
                      >
                      > and it is a rare use of grep, indeed.
                      > --
                      > TZOTZIOY, I speak England very best.
                      > "Be strict when sending and tolerant when receiving." (from RFC1958)
                      > I really should keep that in mind when talking with people, actually...[/color]


                      Comment

                      • Christos TZOTZIOY Georgiou

                        #12
                        Re: Efficient grep using Python? [OT]

                        On Fri, 17 Dec 2004 12:21:08 +0000, rumours say that P@draigBrady.co m
                        might have written:

                        [snip some damn lie aka "benchmark"]

                        [me][color=blue][color=green]
                        >> (Yes, I cheated by adding the F (for no regular expressions) flag :)[/color]
                        >
                        >Also you only have 1000 entries in B!
                        >Try it again with all entries in B also ;-)
                        >Remember the original poster had 100K entries![/color]

                        Well, that's the closest I can do:

                        $ py
                        Python 2.4c1 (#3, Nov 26 2004, 23:39:44)
                        [GCC 3.3.3 (SuSE Linux)] on linux2
                        Type "help", "copyright" , "credits" or "license" for more information.[color=blue][color=green][color=darkred]
                        >>> import sys; sys.ps1='.>>'[/color][/color][/color]
                        ..>> alist=[line.strip() for line in open('/usr/share/dict/words')]
                        ..>> words=set()
                        ..>> for word in alist:
                        .... words.add(word + '\n')
                        .... words.add(word[::-1] + '\n')
                        ....
                        ..>> len(words)
                        90525
                        ..>> words=list(word s)
                        ..>> open('/tmp/A', 'w').writelines (words)
                        ..>> import random; random.shuffle( words)
                        ..>> open('/tmp/B', 'w').writelines (words[:90000])
                        ..>>
                        $ time sort A B B | uniq -u >/dev/null

                        real 0m2.408s
                        user 0m2.437s
                        sys 0m0.037s
                        $ time grep -Fvf B A >/dev/null

                        real 0m1.208s
                        user 0m1.161s
                        sys 0m0.035s

                        What now?-)

                        Mind you, I only replied in the first place because you wrote (my
                        emphasis) "...here is *the* unix way..." and it's the bad days of the
                        month (not mine, actually, but I suffer along...)
                        [color=blue][color=green][color=darkred]
                        >>>>and finally destroys original line
                        >>>>order (should it be important).
                        >>>
                        >>>true[/color]
                        >>
                        >> That's our final agreement :)[/color]
                        >
                        >Note the order is trivial to restore with a
                        >"decorate-sort-undecorate" idiom.[/color]

                        Using python or unix tools (eg 'paste -d', 'sort -k', 'cut -d')?
                        Because the python way has been already discussed by Friedrik, John and
                        Tim, and the unix way gets overly complicated (aka non-trivial) if DSU
                        is involved.

                        BTW, the following occurred to me:

                        tzot@tril/tmp
                        $ cat >A
                        aa
                        ss
                        dd
                        ff
                        gg
                        hh
                        jj
                        kk
                        ll
                        aa
                        tzot@tril/tmp
                        $ cat >B
                        ss
                        ff
                        hh
                        kk
                        tzot@tril/tmp
                        $ sort A B B | uniq -u
                        dd
                        gg
                        jj
                        ll
                        tzot@tril/tmp
                        $ grep -Fvf B A
                        aa
                        dd
                        gg
                        jj
                        ll
                        aa

                        Note that 'aa' is contained twice in the A file (to be filtered by B).
                        So our methods do not produce the same output. As far as the OP wrote:
                        [color=blue]
                        >Essentially, want to do efficient grep, i..e from A remove those lines which
                        >are also present in file B.[/color]

                        grep is the unix way to go for both speed and correctness.

                        I would call this issue a dead horse.
                        --
                        TZOTZIOY, I speak England very best.
                        "Be strict when sending and tolerant when receiving." (from RFC1958)
                        I really should keep that in mind when talking with people, actually...

                        Comment

                        • P@draigBrady.com

                          #13
                          Re: Efficient grep using Python?

                          sf wrote:[color=blue]
                          > The point is that when you have 100,000s of records, this grep becomes
                          > really slow?[/color]

                          There are performance bugs with current versions of grep
                          and multibyte characters that are only getting addressed now.
                          To work around these do `export LANG=C` first.

                          In my experience grep is not scalable since it's O(n^2).
                          See below (note A and B are randomized versions of
                          /usr/share/dict/words (and therefore worst case for the
                          sort method)).

                          $ wc -l A B
                          45427 A
                          45427 B

                          $ export LANG=C

                          $ time grep -Fvf B A
                          real 0m0.437s

                          $ time sort A B B | uniq -u
                          real 0m0.262s

                          $ rpm -q grep coreutils
                          grep-2.5.1-16.1
                          coreutils-4.5.3-19

                          --
                          Pádraig Brady - http://www.pixelbeat.org
                          --

                          Comment

                          • Christos TZOTZIOY Georgiou

                            #14
                            Re: Efficient grep using Python?

                            On Fri, 17 Dec 2004 14:22:34 +0000, rumours say that P@draigBrady.co m
                            might have written:

                            sf:
                            [color=blue]
                            >sf wrote:[color=green]
                            >> The point is that when you have 100,000s of records, this grep becomes
                            >> really slow?[/color]
                            >
                            >There are performance bugs with current versions of grep
                            >and multibyte characters that are only getting addressed now.
                            >To work around these do `export LANG=C` first.[/color]

                            You also should use the -F flag that Pádraig suggests, since you don't
                            have regular expressions in the B file.
                            [color=blue]
                            >In my experience grep is not scalable since it's O(n^2).
                            >See below (note A and B are randomized versions of
                            >/usr/share/dict/words (and therefore worst case for the
                            >sort method)).
                            >
                            >$ wc -l A B
                            > 45427 A
                            > 45427 B
                            >
                            >$ export LANG=C
                            >
                            >$ time grep -Fvf B A
                            >real 0m0.437s
                            >
                            >$ time sort A B B | uniq -u
                            >real 0m0.262s
                            >
                            >$ rpm -q grep coreutils
                            >grep-2.5.1-16.1
                            >coreutils-4.5.3-19[/color]

                            sf, you better do your own benchmarks (there is quick, sample code in
                            other posts of mine and Pádraig's) on your machine, since on my test
                            machine the numbers are reversed re to these of Pádraig's (grep takes
                            half the time).

                            package versions (on SuSE 9.1 64-bit):

                            $ rpm -q grep coreutils
                            grep-2.5.1-427
                            coreutils-5.2.1-21

                            language:
                            $ echo $LANG
                            en_US.UTF-8

                            Caution: both solutions are interexchangeab le as long as you don't have
                            duplicate lines in the A file. If you do, use the grep version.
                            --
                            TZOTZIOY, I speak England very best.
                            "Be strict when sending and tolerant when receiving." (from RFC1958)
                            I really should keep that in mind when talking with people, actually...

                            Comment

                            Working...