最接近值的不同文件,具有不同的行数和其他条件(bash awk 其他)
Closest value different files, with different number of lines and other conditions ( bash awk other)
我必须恢复并修改长文件。
我在两个文件(文件 1 和文件 2)中有两颗星的年龄。星星年龄的那一栏是$1,其余直到$13的栏是我需要在最后打印的信息。
我试图找到一个年龄与恒星年龄相同或最接近的年龄。由于文件太大(~25000 行),我不想在整个数组中搜索速度问题。
此外,它们的行数可能会有很大差异(在某些情况下假设为 ~10000)
我不确定这是否是解决问题的最佳方法,但在没有更好的方法的情况下,这是我的想法。 (如果你有更快更有效的方法,请采纳)
所有数值均保留12位小数。现在我只关心第一列(年龄所在)。
我需要不同的厕所ps。
让我们使用文件 1 中的这个值:
2.326062371284e+05
首先例程应在文件 2 中搜索包含
的所有匹配项
2.3260e+05
(这个循环可能会搜索整个数组,但是如果有办法在到达 2.3261 时停止搜索,那么它会节省一些时间)
如果只找到一个,则输出应该是那个值。
通常会找到好几行,甚至多达1000行。如果是这种情况,它应该重新搜索
2.32606e+05
字里行间创立之前。 (我认为这是一个嵌套循环)
然后匹配的数量将减少到 ~200
在那一刻,例程应该搜索
之间具有一定容差X的最佳差异
2.326062371284e+05
以及所有这 200 行。
这样就有了这些文件
文件 1
1.833800650355e+05 col2f1 col3f1 col4f1
1.959443501406e+05 col2f1 col3f1 col4f1
2.085086352458e+05 col2f1 col3f1 col4f1
2.210729203510e+05 col2f1 col3f1 col4f1
2.326062371284e+05 col2f1 col3f1 col4f1
2.441395539059e+05 col2f1 col3f1 col4f1
2.556728706833e+05 col2f1 col3f1 col4f1
文件 2
2.210729203510e+05 col2f2 col3f2 col4f2
2.354895663228e+05 col2f2 col3f2 col4f2
2.499062122946e+05 col2f2 col3f2 col4f2
2.643228582664e+05 col2f2 col3f2 col4f2
2.787395042382e+05 col2f2 col3f2 col4f2
2.921130362004e+05 col2f2 col3f2 col4f2
3.054865681626e+05 col2f2 col3f2 col4f2
输出文件 3(公差 3000)
2.210729203510e+05 2.210729203510e+05 col2f1 col2f2 col4f1 col3f2
2.326062371284e+05 2.354895663228e+05 col2f1 col2f2 col4f1 col3f2
重要条件:
输出不应该包含重复的行(明星1不能有固定年龄,明星2不同年龄,只是最接近的。
你会如何解决这个问题?
超级感谢!
ps: 我已经完全改变了这个问题,因为它告诉我我的推理有一些错误。谢谢!
Perl 助你一臂之力。这应该非常快,因为它在给定范围内进行二进制搜索。
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use List::Util qw{ max min };
use constant { SIZE => 100,
TOLERANCE => 3000,
};
my @times2;
open my $F2, '<', 'file2' or die $!;
while (<$F2>) {
chomp;
push @times2, $_;
}
my $num = 0;
open my $F1, '<', 'file1' or die $!;
while (my $time = <$F1>) {
chomp $time;
my $from = max(0, $num - SIZE);
my $to = min($#times2, $num + SIZE);
my $between;
while (1) {
$between = int(($from + $to) / 2);
if ($time < $times2[$between] && $to != $between) {
$to = $between;
} elsif ($time > $times2[$between] && $from != $between) {
$from = $between;
} else {
last
}
}
$num++;
if ($from != $to) {
my $f = $time - $times2[$from];
my $t = $times2[$to] - $time;
$between = ($f > $t) ? $to : $from;
}
say "$time $times2[$between]" if TOLERANCE >= abs $times2[$between] - $time;
}
不是 awk 解决方案,其他解决方案也很棒,所以这里是使用 R
的答案
具有不同数据的新答案,这次不是从文件中读取来烘焙示例:
# Sample data for code, use fread to read from file and setnames to name the colmumns accordingly
set.seed(123)
data <- data.table(age=runif(20)*1e6,name=sample(state.name,20),sat=sample(mtcars$cyl,20),dens=sample(DNase$density,20))
data2 <- data.table(age=runif(10)*1e6,name=sample(state.name,10),sat=sample(mtcars$cyl,10),dens=sample(DNase$density,10))
setkey(data,'age') # Set the key for joining to the age column
setkey(data2,'age') # Set the key for joining to the age column
# get the result
result=data[ # To get the whole datas from file 1 and file 2 at end
data2[
data, # Search for each star of list 1
.SD, # return columns of file 2
roll='nearest',by=.EACHI, # Join on each line (left join) and find nearest value
.SDcols=c('age','name','dens')]
][!duplicated(age) & abs(i.age - age) < 1e3,.SD,.SDcols=c('age','i.age','name','i.name','dens','i.dens') ] # filter duplicates in first file and on difference
# Write results to a file (change separator for wish):
write.table(format(result,digits=15,scientific=TRUE),"c:/test.txt",sep=" ")
代码:
# A nice package to have, install.packages('data.table') if it's no present
library(data.table)
# Read the data (the text can be file names)
stars1 <- fread("1.833800650355e+05
1.959443501406e+05
2.085086352458e+05
2.210729203510e+05
2.326062371284e+05
2.441395539059e+05
2.556728706833e+05")
stars2 <- fread("2.210729203510e+05
2.354895663228e+05
2.499062122946e+05
2.643228582664e+05
2.787395042382e+05
2.921130362004e+05
3.054865681626e+05")
# Name the columns (not needed if the file has a header)
colnames(stars1) <- "age"
colnames(stars2) <- "age"
# Key the data tables (for a fast join with binary search later)
setkey(stars1,'age')
setkey(stars2,'age')
# Get the result (more datils below on what is happening here :))
result=stars2[ stars1, age, roll="nearest", by=.EACHI]
# Rename the columns so we acn filter whole result
setnames(result,make.unique(names(result)))
# Final filter on difference
result[abs(age.1 - age) < 3e3]
所以有趣的部分是两个恒星年龄列表中的第一个 'join',在 stars1 中搜索每个在 stars2 中最近的。
这给出(列重命名后):
> result
age age.1
1: 183380.1 221072.9
2: 195944.4 221072.9
3: 208508.6 221072.9
4: 221072.9 221072.9
5: 232606.2 235489.6
6: 244139.6 249906.2
7: 255672.9 249906.2
现在我们每个都有最近的,过滤那些足够接近的(这里的绝对差异大于 3 000):
> result[abs(age.1 - age) < 3e3]
age age.1
1: 221072.9 221072.9
2: 232606.2 235489.6
我必须恢复并
我在两个文件(文件 1 和文件 2)中有两颗星的年龄。星星年龄的那一栏是$1,其余直到$13的栏是我需要在最后打印的信息。
我试图找到一个年龄与恒星年龄相同或最接近的年龄。由于文件太大(~25000 行),我不想在整个数组中搜索速度问题。 此外,它们的行数可能会有很大差异(在某些情况下假设为 ~10000)
我不确定这是否是解决问题的最佳方法,但在没有更好的方法的情况下,这是我的想法。 (如果你有更快更有效的方法,请采纳)
所有数值均保留12位小数。现在我只关心第一列(年龄所在)。
我需要不同的厕所ps。
让我们使用文件 1 中的这个值:
2.326062371284e+05
首先例程应在文件 2 中搜索包含
的所有匹配项2.3260e+05
(这个循环可能会搜索整个数组,但是如果有办法在到达 2.3261 时停止搜索,那么它会节省一些时间)
如果只找到一个,则输出应该是那个值。
通常会找到好几行,甚至多达1000行。如果是这种情况,它应该重新搜索
2.32606e+05
字里行间创立之前。 (我认为这是一个嵌套循环) 然后匹配的数量将减少到 ~200
在那一刻,例程应该搜索
之间具有一定容差X的最佳差异2.326062371284e+05
以及所有这 200 行。
这样就有了这些文件
文件 1
1.833800650355e+05 col2f1 col3f1 col4f1
1.959443501406e+05 col2f1 col3f1 col4f1
2.085086352458e+05 col2f1 col3f1 col4f1
2.210729203510e+05 col2f1 col3f1 col4f1
2.326062371284e+05 col2f1 col3f1 col4f1
2.441395539059e+05 col2f1 col3f1 col4f1
2.556728706833e+05 col2f1 col3f1 col4f1
文件 2
2.210729203510e+05 col2f2 col3f2 col4f2
2.354895663228e+05 col2f2 col3f2 col4f2
2.499062122946e+05 col2f2 col3f2 col4f2
2.643228582664e+05 col2f2 col3f2 col4f2
2.787395042382e+05 col2f2 col3f2 col4f2
2.921130362004e+05 col2f2 col3f2 col4f2
3.054865681626e+05 col2f2 col3f2 col4f2
输出文件 3(公差 3000)
2.210729203510e+05 2.210729203510e+05 col2f1 col2f2 col4f1 col3f2
2.326062371284e+05 2.354895663228e+05 col2f1 col2f2 col4f1 col3f2
重要条件:
输出不应该包含重复的行(明星1不能有固定年龄,明星2不同年龄,只是最接近的。
你会如何解决这个问题?
超级感谢!
ps: 我已经完全改变了这个问题,因为它告诉我我的推理有一些错误。谢谢!
Perl 助你一臂之力。这应该非常快,因为它在给定范围内进行二进制搜索。
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use List::Util qw{ max min };
use constant { SIZE => 100,
TOLERANCE => 3000,
};
my @times2;
open my $F2, '<', 'file2' or die $!;
while (<$F2>) {
chomp;
push @times2, $_;
}
my $num = 0;
open my $F1, '<', 'file1' or die $!;
while (my $time = <$F1>) {
chomp $time;
my $from = max(0, $num - SIZE);
my $to = min($#times2, $num + SIZE);
my $between;
while (1) {
$between = int(($from + $to) / 2);
if ($time < $times2[$between] && $to != $between) {
$to = $between;
} elsif ($time > $times2[$between] && $from != $between) {
$from = $between;
} else {
last
}
}
$num++;
if ($from != $to) {
my $f = $time - $times2[$from];
my $t = $times2[$to] - $time;
$between = ($f > $t) ? $to : $from;
}
say "$time $times2[$between]" if TOLERANCE >= abs $times2[$between] - $time;
}
不是 awk 解决方案,其他解决方案也很棒,所以这里是使用 R
的答案具有不同数据的新答案,这次不是从文件中读取来烘焙示例:
# Sample data for code, use fread to read from file and setnames to name the colmumns accordingly
set.seed(123)
data <- data.table(age=runif(20)*1e6,name=sample(state.name,20),sat=sample(mtcars$cyl,20),dens=sample(DNase$density,20))
data2 <- data.table(age=runif(10)*1e6,name=sample(state.name,10),sat=sample(mtcars$cyl,10),dens=sample(DNase$density,10))
setkey(data,'age') # Set the key for joining to the age column
setkey(data2,'age') # Set the key for joining to the age column
# get the result
result=data[ # To get the whole datas from file 1 and file 2 at end
data2[
data, # Search for each star of list 1
.SD, # return columns of file 2
roll='nearest',by=.EACHI, # Join on each line (left join) and find nearest value
.SDcols=c('age','name','dens')]
][!duplicated(age) & abs(i.age - age) < 1e3,.SD,.SDcols=c('age','i.age','name','i.name','dens','i.dens') ] # filter duplicates in first file and on difference
# Write results to a file (change separator for wish):
write.table(format(result,digits=15,scientific=TRUE),"c:/test.txt",sep=" ")
代码:
# A nice package to have, install.packages('data.table') if it's no present
library(data.table)
# Read the data (the text can be file names)
stars1 <- fread("1.833800650355e+05
1.959443501406e+05
2.085086352458e+05
2.210729203510e+05
2.326062371284e+05
2.441395539059e+05
2.556728706833e+05")
stars2 <- fread("2.210729203510e+05
2.354895663228e+05
2.499062122946e+05
2.643228582664e+05
2.787395042382e+05
2.921130362004e+05
3.054865681626e+05")
# Name the columns (not needed if the file has a header)
colnames(stars1) <- "age"
colnames(stars2) <- "age"
# Key the data tables (for a fast join with binary search later)
setkey(stars1,'age')
setkey(stars2,'age')
# Get the result (more datils below on what is happening here :))
result=stars2[ stars1, age, roll="nearest", by=.EACHI]
# Rename the columns so we acn filter whole result
setnames(result,make.unique(names(result)))
# Final filter on difference
result[abs(age.1 - age) < 3e3]
所以有趣的部分是两个恒星年龄列表中的第一个 'join',在 stars1 中搜索每个在 stars2 中最近的。
这给出(列重命名后):
> result
age age.1
1: 183380.1 221072.9
2: 195944.4 221072.9
3: 208508.6 221072.9
4: 221072.9 221072.9
5: 232606.2 235489.6
6: 244139.6 249906.2
7: 255672.9 249906.2
现在我们每个都有最近的,过滤那些足够接近的(这里的绝对差异大于 3 000):
> result[abs(age.1 - age) < 3e3]
age age.1
1: 221072.9 221072.9
2: 232606.2 235489.6