File Coverage

blib/lib/JSONL/Subset.pm
Criterion Covered Total %
statement 80 81 98.7
branch 39 52 75.0
condition 20 32 62.5
subroutine 8 8 100.0
pod 0 1 0.0
total 147 174 84.4


line stmt bran cond sub pod time code
1             package JSONL::Subset;
2              
3 1     1   108157 use strict;
  1         1  
  1         28  
4 1     1   3 use warnings;
  1         1  
  1         39  
5              
6 1     1   3 use Exporter 'import';
  1         2  
  1         20  
7 1     1   456 use IO::File;
  1         839  
  1         95  
8 1     1   5 use List::Util qw(shuffle);
  1         1  
  1         1006  
9              
10             our @EXPORT_OK = qw(subset_jsonl);
11              
12             sub subset_jsonl {
13 14     14 0 268265 my %args = @_;
14             my ($infile, $outfile, $percent, $lines, $mode, $seed, $streaming) =
15 14         73 @args{qw/infile outfile percent lines mode seed streaming/};
16              
17 14 50 33     151 die "infile, outfile, and percent or lines are required" unless $infile && $outfile && (defined $percent || defined $lines);
      66        
      66        
18 14 50 66     59 die "cannot specify percent and lines, must choose one or the other" if (defined $percent && defined $lines);
19 14 50 33     81 die "percent must be between 0 and 100" if (defined $percent && ($percent < 0 || $percent > 100));
      66        
20 14 50       103 die "Invalid mode: $mode" unless $mode =~ /^(random|start|end)$/;
21              
22 14   50     43 $mode ||= 'random';
23              
24 14 100 66     59 if (!defined $streaming || $streaming == 0) {
25 7         24 _subset_jsonl_inplace(
26             infile => $infile,
27             outfile => $outfile,
28             percent => $percent,
29             lines => $lines,
30             mode => $mode,
31             seed => $seed
32             );
33             } else {
34 7         26 _subset_jsonl_streaming(
35             infile => $infile,
36             outfile => $outfile,
37             percent => $percent,
38             lines => $lines,
39             mode => $mode,
40             seed => $seed
41             );
42             }
43             }
44              
45             sub _subset_jsonl_inplace {
46 7     7   37 my %args = @_;
47             my ($infile, $outfile, $percent, $lines, $mode, $seed) =
48 7         29 @args{qw/infile outfile percent lines mode seed/};
49              
50 7 50       574 my $in = IO::File->new($infile, "<:raw") or die "Can't read $infile: $!";
51 7         5009 my @lines = grep { /^\s*[\{\[]/ } map { $_ } <$in>;
  77         254  
  77         156  
52              
53 7 50 66     53 die "requested more lines ($lines) than infile contains (${scalar(@lines)})" if (defined $lines && $lines > scalar(@lines));
  0         0  
54              
55 7 100       22 if ($mode eq 'random') {
56 3 50       21 srand($seed) if defined $seed;
57 3         47 @lines = shuffle(@lines);
58             }
59              
60 7         14 my $count = 0;
61 7 100       17 if (defined $percent) {
62 4         15 $count = int(@lines * $percent / 100);
63             } else {
64 3         5 $count = $lines;
65             }
66              
67 7 100       37 my @subset = $mode eq 'end'
68             ? @lines[-$count..-1]
69             : @lines[0..$count-1];
70 7 50       50 my $out = IO::File->new($outfile, ">:raw") or die $!;
71              
72 7         1132 for my $line (@subset) {
73 21         163 print $out $line;
74             }
75 7         49 $out->close;
76              
77 7         1059 $in->close;
78             }
79              
80             sub _subset_jsonl_streaming {
81 7     7   37 my %args = @_;
82             my ($infile, $outfile, $percent, $lines, $mode, $seed) =
83 7         33 @args{qw/infile outfile percent lines mode seed/};
84              
85 7 50       56 my $in = IO::File->new($infile, "<:encoding(UTF-8)") or die "Can't read $infile: $!";
86 7         3998 my $total = 0;
87              
88 7         220 while (my $line = <$in>) {
89 77 100       428 $total++ if $line =~ /^\s*[\{\[]/;;
90             }
91              
92 7         141 close $in;
93              
94 7 50 66     35 die "requested more lines ($lines) than infile contains ($total)" if (defined $lines && $lines > $total);
95              
96 7         15 my $count = 0;
97              
98 7 100       18 if (defined $percent) {
99 4         14 $count = int($total * $percent / 100);
100             } else {
101 3         5 $count = $lines;
102             }
103              
104 7         17 my %picked = ();
105              
106 7 100       26 if ($mode eq 'start') {
    100          
107 2         10 %picked = map { $_ => 1 } 0 .. $count-1;
  6         22  
108             } elsif ($mode eq 'end') {
109 2         9 %picked = map { $_ => 1 } ($total-$count) .. ($total-1);
  6         20  
110             } else { # Random
111 3 50       13 srand($seed) if defined $seed;
112              
113 3         11 for (my $i = 0; $i < $total; $i++) {
114 30 100       79 if (rand($total - $i) < $count) {
115 9         23 $picked{$i} = 1;
116 9         14 $count--;
117 9 100       27 last if $count == 0;
118             }
119             }
120             }
121              
122 7 50       309 open $in, "<:raw", $infile or die $!;
123 7 50       587 open my $out, ">:raw", $outfile or die $!;
124 7         24 my $real = 0;
125              
126 7         117 while (my $line = <$in>) {
127 61 100       273 next unless $line =~ /^\s*[\{\[]/;;
128              
129 56 100       217 print $out $line if $picked{$real};
130 56         89 $real++;
131              
132 56 100 100     209 if ($mode eq 'start' && $real >= $count) {
133 2         7 last;
134             }
135             }
136              
137 7         95 close $in;
138 7         7701 close $out;
139             }
140              
141             1;
142              
143             __END__