Несколько входов с MRJob

Я пытаюсь научиться использовать API Python Yelp для MapReduce, MRJob. Их простой пример счетчика слов имеет смысл, но мне любопытно, как можно обрабатывать приложение с несколькими входами. Например, вместо того, чтобы просто считать слова в документе, умножая вектор на матрицу. Я придумал это решение, которое функционирует, но чувствует себя глупо:

class MatrixVectMultiplyTast(MRJob):
    def multiply(self,key,line):
            line = map(float,line.split(" "))
            v,col = line[-1],line[:-1]

            for i in xrange(len(col)):
                    yield i,col[i]*v

    def sum(self,i,occurrences):
            yield i,sum(occurrences)

    def steps(self):
            return [self.mr (self.multiply,self.sum),]

if __name__=="__main__":
    MatrixVectMultiplyTast.run()

этот код ./matrix.py < input.txt и причина, по которой он работает, заключается в том, что матрица хранится во входных данных.txt по столбцам, с соответствующим значением вектора в конце строки.

Итак, следующая матрица и вектор:

enter image description here

представлены как входные данные.txt as:

enter image description here

короче говоря, как бы я мог более естественно хранить матрицу и вектор в отдельных файлах и передавать их в MRJob?

4 ответов


Если вам нужна обработка ваших необработанных данных против другого (или того же набора данных row_i, row_j), вы можете:

1) Создайте ведро S3 для хранения копии ваших данных. Передайте местоположение этой копии классу задач, например self.опции.ведро и себя.варианты.my_datafile_copy_location в коде ниже. Предостережение: к сожалению, кажется, что весь файл должен быть "загружен" на машины задач перед обработкой. Если связь прерывается или занимает слишком много времени для загрузки это задание может завершиться ошибкой. Вот некоторый код Python / MRJob для этого.

поместите это в свою функцию mapper:

d1 = line1.split('\t', 1)
v1, col1 = d1[0], d1[1]
conn = boto.connect_s3(aws_access_key_id=<AWS_ACCESS_KEY_ID>, aws_secret_access_key=<AWS_SECRET_ACCESS_KEY>)
bucket = conn.get_bucket(self.options.bucket)  # bucket = conn.get_bucket(MY_UNIQUE_BUCKET_NAME_AS_STRING)
data_copy = bucket.get_key(self.options.my_datafile_copy_location).get_contents_as_string().rstrip()
### CAVEAT: Needs to get the whole file before processing the rest.
for line2 in data_copy.split('\n'):
    d2 = line2.split('\t', 1)
    v2, col2 = d2[0], d2[1]
    ## Now, insert code to do any operations between v1 and v2 (or c1 and c2) here:
    yield <your output key, value pairs>
conn.close()

2) Создайте домен SimpleDB и сохраните там все свои данные. Читайте здесь на boto и SimpleDB: http://code.google.com/p/boto/wiki/SimpleDbIntro

ваш код картографа будет выглядеть так:

dline = dline.strip()
d0 = dline.split('\t', 1)
v1, c1 = d0[0], d0[1]
sdb = boto.connect_sdb(aws_access_key_id=<AWS_ACCESS_KEY>, aws_secret_access_key=<AWS_SECRET_ACCESS_KEY>)
domain = sdb.get_domain(MY_DOMAIN_STRING_NAME)
for item in domain:
    v2, c2 = item.name, item['column']
    ## Now, insert code to do any operations between v1 and v2 (or c1 and c2) here:
    yield <your output key, value pairs>
sdb.close()

этот второй вариант может работать лучше, если у вас очень большой объем данных, так как он может сделать запросы для каждой строки данных, а не всю сумму сразу. Имейте в виду, что значения SimpleDB могут иметь длину не более 1024 символов, поэтому вам может потребоваться сжать/распаковать с помощью некоторого метода, если ваши значения данных длиннее.


фактический ответ на ваш вопрос заключается в том, что mrjob еще не поддерживает шаблон соединения потоковой передачи hadoop, который должен читать переменную среды map_input_file (которая предоставляет карту.вход.file property), чтобы определить, с каким типом файла вы имеете дело с на основе его пути и/или имени.

вы все еще можете вытащить его, если вы можете легко определить, просто прочитав сами данные, к какому типу он принадлежит, как показано в этом статья:

http://allthingshadoop.com/2011/12/16/simple-hadoop-streaming-tutorial-using-joins-and-keys-with-python/

однако это не всегда возможно...

в противном случае моя работа выглядит фантастически, и я хотел бы, чтобы они могли добавить поддержку для этого в будущем. До тех пор это дело выключатель для меня.


вот как я использую несколько входов и на основе имени файла делаю подходящие изменения в фазе mapper.

Бегунок Программе :

from mrjob.hadoop import *


#Define all arguments

os.environ['HADOOP_HOME'] = '/opt/cloudera/parcels/CDH/lib/hadoop/'
print "HADOOP HOME is now set to : %s" % (str(os.environ.get('HADOOP_HOME')))
job_running_time = datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
hadoop_bin = '/usr/bin/hadoop'
mode = 'hadoop'
hs = HadoopFilesystem([hadoop_bin])

input_file_names = ["hdfs:///app/input_file1/","hdfs:///app/input_file2/"]

aargs = ['-r',mode,'--jobconf','mapred.job.name=JobName','--jobconf','mapred.reduce.tasks=3','--no-output','--hadoop-bin',hadoop_bin]
aargs.extend(input_file_names)
aargs.extend(['-o',output_dir])
print aargs
status_file = True

mr_job = MRJob(args=aargs)
with mr_job.make_runner() as runner:
    runner.run()
os.environ['HADOOP_HOME'] = ''
print "HADOOP HOME is now set to : %s" % (str(os.environ.get('HADOOP_HOME')))

Класс MRJob:

class MR_Job(MRJob):
    DEFAULT_OUTPUT_PROTOCOL = 'repr_value'
    def mapper(self, _, line):
    """
    This function reads lines from file.
    """
    try:
        #Need to clean email.
        input_file_name = get_jobconf_value('map.input.file').split('/')[-2]
                """
                Mapper code
                """
    except Exception, e:
        print e

    def reducer(self, email_id,visitor_id__date_time):
    try:
        """
                Reducer Code
                """
    except:
        pass


if __name__ == '__main__':
    MRV_Email.run()

в моем понимании, вы не будете использовать MrJob, если не хотите использовать кластер Hadoop или службы Hadoop от Amazon, даже если в примере используется работа с локальными файлами.

MrJob в основном использует"потоковая передача Hadoop", чтобы представить работу.

это означает, что все входные данные, указанные как файлы или папки из Hadoop, передаются в mapper и последующие результаты в reducer. All mapper получает фрагмент ввода и считает все входные данные схематически то же самое,так что он равномерно анализирует и обрабатывает ключ, значение для каждого среза данных.

выводя из этого понимания, входные данные схематически одинаковы для картографа. Единственный способ включить два различных схематических данных-это чередовать их в одном файле таким образом, чтобы картограф мог понять, какие данные являются векторными, а какие-матричными.

You are actually doing it already.

Вы можете просто улучшить это, имея некоторый спецификатор, если строка является матричными данными или векторные данные. Как только вы видите векторные данные, к ним применяются предыдущие матричные данные.

matrix, 1, 2, ...
matrix, 2, 4, ...
vector, 3, 4, ...
matrix, 1, 2, ...
.....

но процесс, который вы упомянули, работает хорошо. Все схематические данные должны быть в одном файле.

Это все еще имеет проблемы. K, V map reduce работает лучше, когда полная схема присутствует в одной строке и содержит полный единый блок обработки.

из моего понимания, вы уже делаете это правильно, но я думаю, Map-Reduce не является подходящим механизмом для такого рода данных. Надеюсь, кто-нибудь объяснит это лучше, чем я.