我正在从当前目录中读取许多pdb
个文件,在我专注于特定行的每个文件中,我提出两个条件,如果满足这些条件,那么我试图通过添加重命名该文件从该行到其现有文件名的某些元素。我操作的线条看起来像这样:
HET CA A 800 1
HET SO4 A 901 5
HET SO4 A 902 5
HET SO4 A 903 5
HET RAM A 509 11
HET GTR A 510 12
HET RAM A 511 15
HET GTR A 512 12
HET RAM A 513 15
HET GTR A 514 12
HETNAM CA CALCIUM ION
HETNAM SO4 SULFATE ION
HETNAM RAM ALPHA-L-RHAMNOSE
HETNAM GTR BETA-D-GALACTOPYRANURONIC ACID
HETSYN GTR GALACTURONIC ACID
FORMUL 2 CA CA 2+
FORMUL 3 SO4 3(O4 S 2-)
FORMUL 6 RAM 3(C6 H12 O5 N5)
FORMUL 6 GTR 3(C6 H10 O7)
FORMUL 7 HOH *362(H2 O)
HET
和FORMUL
开头的行。HET
行我正在阅读最后一栏或第四栏。从FORMUL
行开始,我正在阅读最后一栏。HET
行中的数字为>= 6
,而FORMUL
下的数字为C >= 2
&&
sum of O C N
为>= 6
然后我想grep FORMUL
行下的第3个元素并将其放入文件名。例如,从此输入中只有RAM
符合这两个条件,因为15
下的HET
和sum of C O N
中的=>6
FORMUL
所以我期待grep RAM
并将其添加到已处理的文件名中。如下所示:filename_RAM
如果有更多元素满足这两个条件,则会有更多元素添加到filename
我写的脚本看起来像这样:
#! usr/bin/env perl
use autodie;
use warnings;
use strict;
use File::Glob;
my $pdbs;
my $fh;
my @fh;
my @data;
my $c;
my @lines;
my $lines;
my $file_sz;
my $line;
my @colums;
my $colums;
my @het;
my $het;
my $cols;
my @cols;
my %letters;
my @hetnam;
my $hetnam;
foreach my $pdbs (glob '*pdb') #Reading each pdb file from the current directory
{
printf "%s\n", $pdbs;
open my $fh, "<" ,$pdbs; #Read each pdb file into a filehandle
#print "$fh\n";
@lines= <$fh>; #Putting content of each file into an array
$file_sz = scalar @lines;
#print "$file_sz\n";
#print "@lines\n";
for ($c=0; $c<=$#lines; $c++) #Reading each line
{
chomp ($lines[$c]);
if ($lines[$c] =~ m/^HET /) #If line starts with HET
{
my @colums = split ' ', $lines[$c];
# print join "\t", $colums [4];
print "$colums[4]\n"; #Printing only a fifth column (4th element)
push @hetnam, $colums[1];
}
if ($lines[$c] =~ m/^FORMUL /) #If line starts with FORMUL
{
my @cols = split /\s+/, $lines[$c], 4;
next unless $cols[0] eq 'FORMUL';
my %letters = $cols[-1] =~ m/([A-Z])(\d+)/g;
$letters{$lines[$c]} = 0 for (qw[C O N]);
next if $letters{C} <= 2
and $letters{C} + $letters{O} + $letters{N} <= 6;
#print "@cols\n";
}
if ($colums[4]=>6 && $letters{C} <= 2 && $letters{C} + $letters{O} + $letters{N} => 6) #line 61
{
system ("mv $pdbs $pdbs/_$hetnam");
}
}
}
我遇到的错误是:
mv: accessing `4YE1.pdb/_': Not a directory
Use of uninitialized value $letters{"C"} in numeric le (<=) at script1 line 61, <$_[...]> line 5708.
Use of uninitialized value in addition (+) at script1 line 61, <$_[...]> line 5708.
答案 0 :(得分:2)
mv: accessing ``4YE1.pdb/_': Not a directory
system
调用正试图将文件4YE1.pdb
移动(并重命名,它永远不会转到)同名文件夹下的文件mkdir
,它无法找到。如果您打算创建一个与文件同名的文件夹(我强烈反对),您需要先使用Use of uninitialized value $letters{"C"} in numeric le (<=) at script1 line 61, <$_[...]> line 5708
(或等效文件)创建它。
%letters
对if
所做的更改/分配仅存在于my
块的范围内,因为它已使用if
进行了本地化。要将其my
阻止,请移除my %letters;
if ( ... ) { my %letters = ... }
if ( ... ) { next if $letters{C} ... }
。
所以而不是:
my %letters;
if ( ... ) { %letters = ... }
if ( ... ) { next if $letters{C} ... }
将其写为:
import re
def is_valid(x):
regexp = re.compile(r'CHG00')
return regexp.search(x) is not None
df['new_col'] = df['col1'].apply(is_valid)
答案 1 :(得分:1)
您的代码没有尝试实现您描述的逻辑的关键部分,并且在其他地方存在各种小的逻辑错误。这是尝试将其重构为合理惯用的Perl,同时保留尽可能多的原始内容。
我遗漏了实际的重命名功能,并留下了一些调试打印件,以帮助您了解如何在开发过程中尝试帮助自己查看程序正在执行的操作。
#! usr/bin/env perl
use autodie;
use warnings;
use strict;
foreach my $pdb (<*pdb>)
{
printf "# %s\n", $pdb;
open my $fh, "<" ,$pdb;
my %hets;
my @suf;
# Don't slurp the entire file. Just read a line at a time.
for my $line (<$fh>)
{
chomp ($line);
if ($line =~ m/^HET /)
{
my @columns = split ' ', $line;
####print "$columns[4] < 6? ", ($columns[4] < 6 ? "yes" : "no"), "\n";
next if $columns[4] < 6;
# We have a HIT, er, HET -- remember it
$hets{$columns[1]} = $columns[4];
####print "$columns[1] added to hets\n";
}
elsif ($line =~ m/^FORMUL /)
{
my @cols = split /\s+/, $line, 4;
# If this is not in $hets, skip it
####print "$cols[2] in %hets? ", ($hets{$cols[2]} ? "yes" : "no"), "\n";
next unless $hets{$cols[2]};
####print "\$cols[-1] is $cols[-1]\n";
# Initialize these to zero _before_ extracting actual counts
my %letters = (C=>0, O=>0, N=>0);
while ($cols[-1] =~ m/([CON])(\d+)/g)
{
$letters{$1} = $2;
####print "\$letters{$1} = '$2'\n"
}
my $con = $letters{"C"} + $letters{"O"} + $letters{"N"};
# Bug fix: next condition was wrong
# ("next if this OR that" is equivalent to "don't next if this AND that")
if ($letters{"C"}>2 && $con >= 6)
{
push @suf, $cols[2]
}
}
}
if (@suf)
{
print "rename $pdb, ", join("_", $pdb, @suf), "\n";
}
# my %hets and my @suf go out of scope here --
# helps you find bugs and make sure you don't process old results
# from a previous file
}
使用my
将所有变量声明为有效全局1}完全取消了首先使用my
的原因。变量应该在您实际使用它的范围内声明,然后在它超出范围时被遗忘。这对于初学者来说很难,但是可以帮助你排队,因为你不能通过在不同的地方为不同的事物使用相同的变量来绊倒你自己的脚,或者对同一循环的前一次迭代中的旧值进行操作(值将会从undef
声明中my
,直到您实际给它一个值)。当然,在封闭的范围内再次使用my
声明全局变量然后再次使用my
重新声明它们不仅毫无意义,而且确实完全令人困惑。
除了实际实现您的要求中的许多条件而不是代码中的条件之外,这里的主要变化是在处理文件中的行时收集匹配的后缀,然后只有在我们处理完后才尝试重命名它们整个文件。
我修改了代码,希望在评论中解释我的想法。语义是相同的:这不是重构,我不知道逻辑是否正确。
use strict;
use warnings;
use autodie;
for my $pdb ( glob '*pdb' ) {
printf "# %s\n", $pdb;
open my $fh, "<", $pdb;
my %hets;
my @suf;
for my $line ( <$fh> ) {
chomp( $line );
if ( $line =~ m/^HET / ) {
my @columns = split ' ', $line;
next if $columns[4] < 6;
$hets{ $columns[1] } = $columns[4];
}
elsif ( $line =~ m/^FORMUL / ) {
my @cols = split /\s+/, $line, 4;
next unless $hets{ $cols[2] };
my %letters = ( C => 0, O => 0, N => 0 );
while ( $cols[-1] =~ m/([CON])(\d+)/g ) {
$letters{$1} = $2;
}
my $con = $letters{"C"} + $letters{"O"} + $letters{"N"};
if ( $letters{"C"} > 2 && $con >= 6 ) {
push @suf, $cols[2];
}
}
}
if ( @suf ) {
print "rename $pdb, ", join( "_", $pdb, @suf ), "\n";
}
}
答案 2 :(得分:0)
可能是系统调用中的shell转义,或者是如何设置%letters
的错误。
使用内置的rename或核心模块File::Copy可能会有更好的成功。您还可以将系统调用更改为3个参数,一个用于进程mv
,另一个用于源和目标。但我认为你应该只使用前两个选项。
等等,我发现你正试图从$pdbs
转到$pdbs/_$hetname
。这将是棘手的,因为文件存在,然后你尝试将它移动到一个有自己名字的路径,一个目录应该是什么?这将需要多个步骤,例如:
rename($pdbs, "$pdbs_$hetname");
mkdir($pdbs);
rename("$pdbs_$hetname", "$pdbs/_$hetname");
除了我希望每个$pdbs
都是唯一的,否则这将在下一次失败。
答案 3 :(得分:0)
我无法确定修复,因为您还没有详细描述您的代码应该做什么。但FORMUL
的代码块没有意义,我认为它应该是这样的
if ( $lines[$c] =~ m/^FORMUL\b/ ) {
my @cols = split ' ', $lines[$c], 4;
my %letters = ( C => 0, O => 0, N => 0 );
$letters{$1} = $2 while $cols[-1] =~ m/([A-Z])(\d+)/g;
next if $letters{C} <= 2
and $letters{C} + $letters{O} + $letters{N} <= 6;
}
另请注意您的移动操作
system ("mv $pdbs $pdbs/_$hetnam")
要求系统创建一个与已存在的文件同名的目录,这将失败。您可以使用tempdir
中的File::Temp
:
use File:;Temp 'tempdir';
my $newdir = tempdir;
rename $pdbs, "$newdir/_$hetnam";
rename $newdir, $pdbs;
但请注意,您从未为$hetnam
根据底层平台,您可能需要将rename
替换为move
来自File::Copy
答案 4 :(得分:0)
这是一个快速尝试重新实现你在shell + Awk中描述的内容。
#!/bin/bash
for file in *pdb; do
if suffixes=$(awk 'BEGIN { suf = "" }
# If first column is HET and number >= 6, remember this one
$1 == "HET" && $5 >= 6 { het[$2] = $5 }
# If first column is FORMUL and this is a HET we remembered ...
$1 == "FORMUL" && ($3 in het) {
# If there are parentheses, trim them and anything outside
sub(/^.*\(/, "", $4);
sub(/\).*/, "", $NF);
# Now sum O, C, and N entries, but abort if C <= 2 or missing
sum = 0
c = 0
for(i=4; i<=NF; ++i) {
if ($i ~ /^[OCN]/) {
n = 0 + substr($i, 2)
if ($i ~ /^C/} {
if (n <= 2) next;
c=1;
}
sum += n
}
}
# If we did not see any C, abort this line
if (!c) next;
# Now if sum >= 6, add suffix
if (sum >= 6) suf = suf "_" $3
}
# We are done. Print result if any.
# Otherwise "exit 1" will cause the calling "if" to fail.
END { if (suf) { print suf; exit 0 } exit 1 }' "$file")
then
mv "$file" "$file$suffixes"
fi
done
这会为您的测试数据生成后缀_RAM_GTR
,如果您的标准正确明确,我不明白为什么不应包含GTR
。
我将从评论中重申,您遇到的一个错误是您在目标文件名中的下划线之前错误添加的斜杠。你无法将文件重命名为目录名,这就是斜杠在Unix文件名中指定的内容。