Как сравнить информацию из разных файлов в MapReduce?

Цель состоит в том, чтобы узнать, насколько файл X похож на файлы y1, y2, ..., yn.

Для каждого файла я извлекаю информацию и сохраняю ее в структурах; скажем, из файла я подсчитываю количество слов и сохраняю результаты в HashMap<String, Integer> wordCount (есть другие структуры, хранящие другую информацию).

Поэтому мне нужно сгенерировать wordCount из fileX; извлечь wordCount из файла Y (предварительно сгенерированного и записанного в файлы HDFS); подсчитайте, насколько эти два количества слов похожи (я не могу сделать построчную разницу, мне нужно сходство в процентах).

FileX исправлен, и его необходимо сравнить с N файломY.

Итак, моя идея была:

Job1: вычислить информацию fileX и записать ее в HDFS.

Job2 (chainMapper карты1-карты2):

Map1: прочитать HashMap<String, Integer> wordCount файла X; передача структур в Map2.

Map2: получает 2 входа, структуры файла X, путь к каталогу файлов Y.

Map2 вычисляет сходство HashMap<String, Integer> wordCountX и HashMap<String, Integer> wordCountY; редуктор получает все значения сходства и упорядочивает их.

Я читал на Hadoop - The definitive guide of Tom White и в Интернете о MultipleInputs, но речь идет не о двух входах в 1 маппер, а о том, чтобы различать мапперы на основе входных данных. Поэтому я хочу спросить, как переслать два значения одному картографу; Я рассматривал возможность использования распределенного кеша, но это бесполезно для решения этой проблемы; и, наконец, как убедиться, что каждый преобразователь получает отдельный файлY.

Я пытался обновить глобальную HashMap<String, Integer> wordCount, но когда начинается новое задание, оно не может получить доступ к этой структуре (или, лучше, она пуста).

public class Matching extends Configured implements Tool{

    private static HashMap<String, Integer> wordCountX;

    public static void main(String[] args) throws Exception {

        int res = ToolRunner.run(new Matching(), args);
        System.exit(res);

    } //end main class

    public int run(String[] args) throws Exception {
        ...
    }

}

Обновлено:

Ответ вот это хорошее решение.

Я добавляю полученный фрагмент кода.

Запуск работы:

//configuration and launch of job
        Job search = Job.getInstance(getConf(), "2. Merging and searching");
        search.setJarByClass(this.getClass());

        MultipleInputs.addInputPath(search, creationPath, TextInputFormat.class);
        MultipleInputs.addInputPath(search, toMatchPath, TextInputFormat.class);

        FileOutputFormat.setOutputPath(search, resultPath);
        search.setNumReduceTasks(Integer.parseInt(args[2]));

        search.setMapperClass(Map.class);
        search.setReducerClass(Reduce.class);

        search.setMapOutputKeyClass(ValuesGenerated.class);
        search.setMapOutputValueClass(IntWritable.class);
        //TODO
        search.setOutputKeyClass(NullWritable.class);
        search.setOutputValueClass(Text.class);

        return search.waitForCompletion(true) ? 0 : 1;

Слияние карт (на этапе очистки):

@Override
        public void cleanup(Context context) throws IOException, InterruptedException {

            InputSplit split = context.getInputSplit();
            Class<? extends InputSplit> splitClass = split.getClass();

            FileSplit fileSplit = null;
            if (splitClass.equals(FileSplit.class)) {
                fileSplit = (FileSplit) split;
            } else if (splitClass.getName().equals(
                    "org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")) {
                // begin reflection hackery...

                try {
                    Method getInputSplitMethod = splitClass
                            .getDeclaredMethod("getInputSplit");
                    getInputSplitMethod.setAccessible(true);
                    fileSplit = (FileSplit) getInputSplitMethod.invoke(split);
                } catch (Exception e) {
                    // wrap and re-throw error
                    throw new IOException(e);
                }

                // end reflection hackery
            }

            String filename = fileSplit.getPath().getName();
            boolean isKnown;

            /*
            the two input files are nominated dinamically;
            the file0 has some name "023901.txt",
            the file1 is the output of a precedent MR job, and is
            something like "chars-r-000000"
            */
            if (filename.contains(".txt")) {
                isKnown = false;
            }
            else {
                isKnown = true;
            }

            if (isKnown) { //file1, known

                ValuesGenerated.setName(new Text(name));

                //other values set
                //...

                context.write(ValuesGenerated, new IntWritable(1));

            }
            else { //file0, unknown

                ValuesGenerated.setName(new Text("unknown"));

                //other values set
                //...

                context.write(ValuesGenerated, new IntWritable(0));

            }           
        }

Уменьшить фазу:

public static class Reduce extends Reducer<ValuesGenerated, IntWritable, NullWritable, Text> {

            @Override
            public void reduce(ValuesGenerated key, Iterable<IntWritable> values, Context context) 
                    throws IOException, InterruptedException {

                ValuesGenerated known;
                ValuesGenerated unk;

                String toEmit = null;

                for (IntWritable value : values) {

                    if (value.get() == 1) { //known
                        known = key;
                        toEmit = key.toString();
                        toEmit += "\n " + value;
                        context.write(NullWritable.get(), new Text(toEmit));
                    }
                    else { //unknown
                        unk = key;
                        toEmit = key.toString();
                        toEmit += "\n " + value;
                        context.write(NullWritable.get(), new Text(toEmit));
                    }

                }

            }//end reduce

        } //end Reduce class

Я столкнулся с другой проблемой, но я обошел ее с помощью этого решения Hadoop MultipleInputs завершается с ошибкой ClassCastException

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
335
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вместо глобального вы можете использовать БД или даже писать в файл.

Проверьте соотношение частот к размеру HashMaps и сравните:

HashMap<String, Integer> similarities = new HashMap<String, Integer>();
int matching = 0
Int totalX = getTotal(wordCountX);
int totalY = getTotal(wordCountY);

wordCountX.forEach((k,v)->{ 
     Integer count = wordCountY.get(k);
    if (count.getIntValue() / totalY == v.getIntValue() / totalX)
        similarities.put(k, Integer.valueOf(v.getIntValue() / totalY);
});

Для сходства ваше решение неверно, из-за самой структуры слова; два файла всегда имеют разные значения для одного и того же ключа; например, в txt1 100 слов, 50% из них «X», в txt2 200 слов, 50% из них «X», я говорю, что txt1 и txt2 очень похожи на «X».

Pleasant94 20.04.2019 14:02

Я отредактировал свой ответ. Какое приложение вы делаете?

nohnce 21.04.2019 18:59

Цель состоит в том, чтобы получить процент от того, насколько два файла имеют одинаковое число слов.

Pleasant94 21.04.2019 21:36
Ответ принят как подходящий

Вы можете иметь несколько входных файлов в один и тот же Mapper, просто добавив несколько входных путей к файлам. Затем вы можете использовать mapperContext, чтобы определить, какое разделение файлов происходит из какого местоположения файла.

Итак, в основном,

Шаг 1: работа MR

  • Читать файл 1+2

  • В Mapper выдает <word, [val1, val2]> (значение1 равно 1, если файл разделен из файла1 и 0 в противном случае; аналогично для значения2)

  • в редьюсере напишите hashmap <work, [file1_count, file2_count]>

Шаг 2: объедините осколки (количество слов не может быть таким большим и должно поместиться на одной машине) и используйте простое задание Java для создания пользовательской метрики сходства.

Другие вопросы по теме