Группировка пакета данных по идентичным значениям в свинье

Я создал следующий скрипт Pig для фильтрации предложений из коллекции веб-документов (Common Crawl), в которых упоминается название фильма (из предопределенного файла данных названий фильмов), применения анализа настроений к этим предложениям и группировки этих настроений по фильмам.

register ../commoncrawl-examples/lib/*.jar; 
set mapred.task.timeout= 1000;
register ../commoncrawl-examples/dist/lib/commoncrawl-examples-1.0.1-HM.jar;
register ../dist/lib/movierankings-1.jar
register ../lib/piggybank.jar;
register ../lib/stanford-corenlp-full-2014-01-04/stanford-corenlp-3.3.1.jar;
register ../lib/stanford-corenlp-full-2014-01-04/stanford-corenlp-3.3.1-models.jar;
register ../lib/stanford-corenlp-full-2014-01-04/ejml-0.23.jar;
register ../lib/stanford-corenlp-full-2014-01-04/joda-time.jar;
register ../lib/stanford-corenlp-full-2014-01-04/jollyday.jar;
register ../lib/stanford-corenlp-full-2014-01-04/xom.jar;

DEFINE IsNotWord com.moviereviewsentimentrankings.IsNotWord;
DEFINE IsMovieDocument com.moviereviewsentimentrankings.IsMovieDocument;
DEFINE ToSentenceMoviePairs com.moviereviewsentimentrankings.ToSentenceMoviePairs;
DEFINE ToSentiment com.moviereviewsentimentrankings.ToSentiment;
DEFINE MoviesInDocument com.moviereviewsentimentrankings.MoviesInDocument;

DEFINE SequenceFileLoader org.apache.pig.piggybank.storage.SequenceFileLoader();

-- LOAD pages, movies and words
pages = LOAD '../data/textData-*' USING SequenceFileLoader as (url:chararray, content:chararray);
movies_fltr_grp = LOAD '../data/movie_fltr_grp_2/part-*' as (group: chararray,movies_fltr: {(movie: chararray)});

-- FILTER pages containing movie
movie_pages = FILTER pages BY IsMovieDocument(content, movies_fltr_grp.movies_fltr);

-- SPLIT pages containing movie in sentences and create movie-sentence pairs
movie_sentences = FOREACH movie_pages GENERATE flatten(ToSentenceMoviePairs(content, movies_fltr_grp.movies_fltr)) as (content:chararray, movie:chararray);

-- Calculate sentiment for each movie-sentence pair
movie_sentiment = FOREACH movie_sentences GENERATE flatten(ToSentiment(movie, content)) as (movie:chararray, sentiment:int);

-- GROUP movie-sentiment pairs by movie
movie_sentiment_grp_tups = GROUP movie_sentiment BY movie;

-- Reformat and print movie-sentiment pairs
movie_sentiment_grp = FOREACH movie_sentiment_grp_tups GENERATE group, movie_sentiment.sentiment AS sentiments:{(sentiment: int)};
describe movie_sentiment_grp;

Тестовые запуски на небольшом подмножестве веб-сканирования показали, что они успешно дают мне пары названия фильма с набором данных целых чисел (от 1 до 5, представляющих очень отрицательное, отрицательное, нейтральное, положительное и очень положительное). В качестве последнего шага я хотел бы преобразовать эти данные в пару названий фильмов и набор данных, содержащий кортежи со всеми различными целыми числами, существующими для этого названия фильма, и их количество. Описание movie_sentiment_grp в конце скрипта возвращает:

movie_sentiment_grp: {group: chararray,sentiments: {(sentiment: int)}}

Так что в основном мне, вероятно, нужно FOREACH по каждому элементу movie_sentiment_grp и GROUP набор данных настроений в группы идентичных значений, а затем использовать функцию COUNT(), чтобы получить количество элементов в каждой группе. Однако мне не удалось найти ничего о том, как сгруппировать набор целых чисел в группы одинаковых значений. Кто-нибудь знает как это сделать?

Фиктивный раствор:

movie_sentiment_grp_cnt = FOREACH movie_sentiment_grp{
    sentiments_grp = GROUP sentiments BY ?;
}

person Niek Tax    schedule 19.01.2014    source источник
comment
В качестве подсказки вы должны попытаться ограничить информацию в своем вопросе только тем, что имеет отношение к проблеме. Все, что выше схемы movie_sentiment_grp, не имеет значения для реальной проблемы. Наличие всей этой дополнительной информации загромождает хороший вопрос.   -  person mr2ert    schedule 29.01.2014


Ответы (2)


Ознакомьтесь с пользовательской функцией CountEach. из Apache DataFu. Учитывая сумку, он создаст новую сумку различных кортежей с добавлением счетчика к каждому соответствующему кортежу.

Пример из документации должен прояснить это:

DEFINE CountEachFlatten datafu.pig.bags.CountEach('flatten');

-- input: 
-- ({(A),(A),(C),(B)})
input = LOAD 'input' AS (B: bag {T: tuple(alpha:CHARARRAY, numeric:INT)});

-- output_flatten: 
-- ({(A,2),(C,1),(B,1)})
output_flatten = FOREACH input GENERATE CountEachFlatten(B);

Для вашего случая:

DEFINE CountEachFlatten datafu.pig.bags.CountEach('flatten');

movie_sentiment_grp_cnt = FOREACH movie_sentiment_grp GENERATE
     group,
     CountEach(sentiments);
person matterhayes    schedule 31.01.2014

Вы были на правильном пути. movie_sentiment_grp имеет правильный формат, и вложенный FOREACH был бы правильным, за исключением того, что вы не можете использовать в нем GROUP. Решение состоит в использовании UDF. Что-то вроде этого:

myudfs.py

#!/usr/bin/python

@outputSchema('sentiments: {(sentiment:int, count:int)}')
def count_sentiments(BAG):
    res = {}
    for s in BAG:
        if s in res:
            res[s] += 1
        else:
            res[s] = 1
    return res.items()

Этот UDF используется как:

Register 'myudfs.py' using jython as myfuncs;

movie_sentiment_grp_cnt = FOREACH movie_sentiment_grp 
                          GENERATE group, myfuncs.count_sentiments(sentiments) ;
person mr2ert    schedule 29.01.2014