Как я могу посчитать количество записей в столбце на основе отдельных записей другого столбца - PullRequest
1 голос
/ 08 июля 2011

Мои данные в файле, как показано ниже, с несколькими столбцами:

A                B             

Tiger         Animal         
Parrot        Bird
Lion          Animal
Elephant      Animal
Crow          Bird
Horse         Animal
Man           Human
Dog           Animal

Я хочу найти количество записей в столбце A, соответствующих различным записям в столбце B. Если возможно, в R или может бытьPerl-скрипт для этого.

Вывести как:

Animal 5
Bird   2
Human  1

Более того, если возможно, узнать, повторялись ли записи в столбце A для различных записей в столбце B, например

A              B   
Tiger         Animal         
Tiger         Animal

Ответы [ 10 ]

5 голосов
/ 08 июля 2011

tapply от базы R решит это хорошо.

with(anm, tapply(A, B, function(x) length(unique(x))))
3 голосов
/ 08 июля 2011

Это решение сделано в R. Это то, что вы искали?

> anm <- data.frame(A = c("Tiger", "Parrot", "Lion", "Elephant", "Crow", "Horse", "Man", "Dog", "Tiger"),
+       B = c("Animal", "Bird", "Animal", "Animal", "Bird", "Animal", "Human", "Animal", "Animal"))
> anm
         A      B
1    Tiger Animal
2   Parrot   Bird
3     Lion Animal
4 Elephant Animal
5     Crow   Bird
6    Horse Animal
7      Man  Human
8      Dog Animal
9    Tiger Animal
> (col.anm <- colSums(table(anm)))
Animal   Bird  Human 
     6      2      1 
> table(anm)
          B
A          Animal Bird Human
  Crow          0    1     0
  Dog           1    0     0
  Elephant      1    0     0
  Horse         1    0     0
  Lion          1    0     0
  Man           0    0     1
  Parrot        0    1     0
  Tiger         2    0     0 # you can see how many times entry from A comes up

EDIT

Чтобы получить желаемый формат вывода, как отмечено в комментарии, оберните ваш результат в data.frame.

> data.frame(col.anm)
       col.anm
Animal       6
Bird         2
Human        1
2 голосов
/ 12 ноября 2012

На случай, если кто-нибудь еще зайдет сюда, вот еще пара подходящих подходов:

myout   <-  lapply(split(anm,list(anm$B)),function(x)
            list(length(unique(x[,"A"])),x[duplicated(x),"A"])
        )
unlist(sapply(myout,function(x)x[1])) # counts in each category
sapply(myout,function(x)x[-1]) # list of duplicated names

или ....

library(data.table)
mydt <- data.table(anm,key="B")
mydt[,.N,by=key(mydt)]
mydt[,.N,by="B,A"][N>1]

где ....

anm = read.table(textConnection(
    "Tiger    Animal         
    Parrot    Bird
    Lion      Animal
    Elephant  Animal
    Crow      Bird
    Horse     Animal
    Man       Human
    Dog       Animal
    Tiger     Animal"))
names(anm) <- c("A","B")

РЕДАКТИРОВАТЬ: Отредактировано в ответ на комментарий Мэтью Доул (автор data.table).

2 голосов
/ 08 июля 2011

Не уверен, что получаю полную структуру данных в файле, но если вы работаете в UNIX:

tr -s ' ' | sort -u | awk '{ print $2}' | sort | uniq -c

<code>
5 Animal
2 Bird
1 Human

Вышеприведенное работает, даже если я добавлю эту строку: «Tiger Animal» в конце, из-за первой сортировки -u.

tr -s выжимает несколько пробелов (поэтому команды сортировки действуют как и ожидалось)

2 голосов
/ 08 июля 2011

Если ваши данные в R, вы можете использовать table(), чтобы получить то, что вам нужно.Сначала приведем несколько примеров данных:

dat <- data.frame(A=c("tiger","parrot","lion","tiger"),B=c("animal","bird","animal","animal"))

Затем мы можем получить счетчики B с:

table(dat$B)

и счетчики с:

table(dat)

Чтобы получить указанную вами таблицу, мы можем использовать пакет plyr:

library("plyr")
tab <- ddply(dat,.(A,B),nrow)
tab[tab$V1>1,]
      A      B V1
3 tiger animal  2
1 голос
/ 08 июля 2011

В Perl (strict и warnings подразумевается.)

my ( %uniq, %count_for );
# here $fh = some input source
while ( <$fh> ) {
    s/^\s+//; # trim left
    s/\s*$//; # trim right (and chomp)
    # This split allows for spaces between words in a single column
    # allows also for tab-delimited record
    my @cols = split /(?:\t|\s{2,})/;
    # Normalize the text and test for uniqueness:
    #
    # By these manipulations: 
    #     Tiger   Animal
    # matches
    #     Tiger      Animal
    # for any column irregularities
    next if $uniq{join('-',@cols)};

    # count occurrence.
    $count_for{$cols[1]}++;
}
1 голос
/ 08 июля 2011

Если вам удобнее работать с SQL, вот очень короткое решение с использованием пакета sqldf в R:

anm <- data.frame(A = c("Tiger", "Parrot", "Lion", "Elephant", "Crow", "Horse", "Man", "Dog", "Tiger"),
      B = c("Animal", "Bird", "Animal", "Animal", "Bird", "Animal", "Human", "Animal", "Animal"))

library(sqldf)      
sqldf("select B,count(distinct A) tot from anm group by B")

sqldf("select B,A,count(*) num from anm group by B,A HAVING num > 1")
1 голос
/ 08 июля 2011

Вот подход с использованием пакета plyr в R.

mydf = read.table(textConnection(
"Tiger    Animal         
Parrot    Bird
Lion      Animal
Elephant  Animal
Crow      Bird
Horse     Animal
Man       Human
Dog       Animal
Tiger     Animal"))

library(plyr)
ddply(mydf, .(V2), summarize, V3 = length(V1))

    V2    V3
1 Animal  6
2   Bird  2
3  Human  1

ddply(mydf, .(V2, V1), summarize, V3 = length(V1))

    V2         V1  V3
1 Animal      Dog  1
2 Animal Elephant  1
3 Animal    Horse  1
4 Animal     Lion  1
5 Animal    Tiger  2
6   Bird     Crow  1
7   Bird   Parrot  1
8  Human      Man  1

EDIT. Добавляет имена животных в каждой категории

 ddply(mydf, .(V2), summarize, 
    V3 = length(V1), 
    V4 = do.call("paste", as.list(unique(V1))))

      V2 V3                            V4
1 Animal  6 Tiger Lion Elephant Horse Dog
2   Bird  2                   Parrot Crow
3  Human  1                           Man
1 голос
/ 08 июля 2011

Первое можно легко сделать с помощью awk:

awk '{ myarray[$2]++ } END { for ( key in myarray ) { print key ": " myarray[key] } }' FILE

Второе немного сложнее ... (http://ideone.com/xdKcs)

awk '{ myarray[$2]++ ; myarray2[$2, $1]++ } 
     END { for ( key in myarray ) { print key ": " myarray[key] } 
           print
           print "Duplicates: "
           for (key in myarray2) { 
               split(key,sep,SUBSEP)
               if (myarray2[sep[1], sep[2]]>1)
                   { print sep[1] ": " sep[2] " " myarray2[sep[1], sep[2]]
     }}}' FILE
0 голосов
/ 08 июля 2011
#!/usr/bin/env perl

use strict;
use warnings;
use File::Slurp qw(slurp);

exit unless $ARGV[0];

my @data = slurp($ARGV[0]);
my (%h);

for (@data) {
  chomp;
  map { next if /^(A|B)$/; $h{$_}++ } split ' ', $_;
}

map { print $_, ": ", $h{$_}, "\n" } keys %h;

использование:

$ perl script.pl columns.txt
...