Сопоставление данных на основе столбца и условий

У меня есть два набора данных с миллионами строк. Пример выглядит так:

Набор данных 1:

Row    col1 col2        col3
 1     A   01-01-1991   10
 2     B   02-01-1991   20

Набор данных 2:

Row    col1 col2        col3
 1     A   01-01-1991   -10
 2     B   02-01-1991   -10
 3     B   01-01-1991   -10      

Я хочу сопоставить строки на основе col1, col2 - с допуском 1 день, и если сумма col3 равна нулю, тогда сгенерируйте уникальный идентификатор для данных.

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

Data_set  Row    col1 col2        col3   Group_Id
    1      1     A   01-01-1991   -10     1
    2      1     A   01-01-1991    10     1 
    1      2     B   02-01-1991    20     2
    1      2     B   02-01-1991   -10     2
    1      3     B   01-01-1991   -10     2

Я не хочу никакого кода, кроме идей. Я прошу вас, ребята, указать мне на хорошую логику для достижения этого для большого набора данных. Я открыт для использования Julia, pyspark или scala.

Я пробовал одну логику:

Чтобы агрегировать базу данных по столбцам col1 и col2, а затем добавить col3 из обоих наборов данных. Это касается группы с идентификатором 1, но группа 2 в нее не попадает.

Не совсем понятно, какие у вас ограничения. Но если вы можете отсортировать набор данных 1 и набор данных 2 на col1 и col2 (в Julia вы могли бы сделать это, например, используя JuliaDB, если данные слишком велики, чтобы поместиться в ОЗУ), тогда напишите цикл, повторяющий оба набора для создания последовательных групп. Для этого нет необходимости загружать целые наборы данных в ОЗУ или сохранять результат в ОЗУ.

Bogumił Kamiński 09.09.2018 11:12

Спасибо @ BogumiłKamiński за комментарий, но как зафиксировать допуск по дате? также существует ли эквивалент merge_asof () Julia, который находится в python?

ty13991 09.09.2018 20:08

Как я предложил, если у вас действительно большие данные, которые не помещаются в памяти, используйте цикл while и перемещайте курсор по обеим таблицам, отслеживая начало и конец двухдневного окна в каждой таблице.

Bogumił Kamiński 09.09.2018 21:18

@ BogumiłKamiński juliadb - хороший вариант. Я пробовал использовать функцию фильтра, но продолжаю получать эту ошибку: ERROR: MethodError: no method matching convert (:: Type {Array {Bool, 1}}, :: PooledArrays.PooledArray {Boo l, UInt8,1, Array { UInt8,1}}, :: Bool) Это могло произойти из-за вызова конструктора Array {Bool, 1} (...), поскольку конструкторы типов возвращаются к преобразованию методов. .... Я пытаюсь фильтровать по col1 .... filter (x -> x == "A", DataA, select =: col1)

ty13991 11.09.2018 01:47

Наверное, было бы хорошо, если бы вы разместили отдельный вопрос об этой ошибке с минимально воспроизводимым кодом, так как сейчас ее сложно диагностировать.

Bogumił Kamiński 11.09.2018 09:03
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
5
149
2

Ответы 2

Итак, у Джулии есть isapporx функция, но это не работает для Dates.

Однако вы можете просто вычесть даты и проверить, меньше ли разница 1.

using Dates
x = Date(2016, 07, 16)
y = Date(2016, 07, 13)
abs( x - y ) # 3 days
abs(x - y) <= Day(1) # false

Для большого набора данных вы можете сначала выделить новую таблицу данных, выяснив, сколько записей она будет иметь, а затем заполнив ее (то есть двухпроходный алгоритм ваших данных)

using DataFrames
A = ... # dataframe 1
B = ... # dataframe 2
f(a::Date, b::Date) = abs(a-b) <= Day(1)
validrows = f.(A[:col2], B[:col2]) # a bitarray
sum(validrows) # number of rows that are within a day of each other

Предполагается, что вы просто сравниваете строку i в наборе данных 1 со строкой i в наборе данных 2. Затем вы можете перебрать строки обоих и «импортировать» их в набор данных 3.

C = DataFrame([Int, Int, Char, Date, ...], [:dataset, :row, :date, ...], sum(validrows))
for i in findall(validrows)
   C[i, :] = ... # your magic here 
end

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

Ваше решение предполагает сопоставление строка за строкой и, в частности, что оба набора данных имеют одинаковое количество строк - что, если я правильно понимаю проблему, - это не так. Кроме того, DataFrame - хорошее решение, только если данных мало (и из описания проблемы я понял, что данные не помещаются в ОЗУ).

Bogumił Kamiński 09.09.2018 21:21

Что касается построчного комментария, это правда, и я сказал об этом. Даже если логика сравнения более сложна, предварительное выделение, вероятно, будет полезным, поскольку накладные расходы на добавление новых строк могут легко превысить логику сравнения / col3. Более того, DataFrames действительно исчерпывает память, правда. Но пара миллионов здесь и там могут легко поместиться в большую часть оперативной памяти, даже на ноутбуках. Но, принимая во внимание, JuliaDB был бы предпочтительнее.

stillearningsomething 09.09.2018 22:11

Ваша основная проблема - логическое сравнение с датами. Я предлагаю вам преобразовать даты в юлианский формат даты. Вот код от Mathematica, который может вам помочь.

ToJulianDayNumber[date_List] := Module[
  {year = date[[1]],
   month = date[[2]],
   day = date[[3]],
   a,
   y,
   m,
   result},
  a = Quotient[    14 - month, 12    ];
  y = year + 4800 - a;
  m = month + 12 * a - 3;
  result = 
   day + Quotient[    153* m + 2, 5    ] + 365 * y + 
    Quotient[    y, 4    ] - Quotient[    y, 100    ] + 
    Quotient[    y, 400    ] - 32045;
  result
  ]

FromJulianDayNumber[juliandaynum_Integer] := Module[
  {gnum, dgnum, cnum, dcnum, bnum,
   dbnum, anum, danum, ynum, mnum, dnum,
   yyyy, mm, dd, jnum, result},
  jnum = juliandaynum + 32044;
  gnum = Quotient[    jnum, 146097    ];
  dgnum = Mod[    jnum, 146097    ];
  cnum = (Quotient[    dgnum, 36524    ] + 1 ) * 3;
  cnum = Quotient[    cnum, 4    ];
  dcnum = dgnum - (cnum * 36524);
  bnum = Quotient[    dcnum, 1461    ];
  dbnum = Mod[    dcnum, 1461    ];
  anum = (Quotient[    dbnum, 365    ] + 1 ) * 3;
  anum = Quotient[    anum, 4    ];
  danum = dbnum - (anum * 365);
  ynum = gnum * 400 + cnum * 100 + bnum * 4 + anum;
  mnum = Quotient[(danum*5 + 308), 153] - 2; 
  dd = danum - Quotient[    (mnum + 4)*153, 5    ] + 123; 
  yyyy = ynum - 4800 + Quotient[    mnum + 2, 12    ];
  mm = Mod[  mnum + 2, 12  ] + 1;
  Return[{yyyy, mm, dd}]
  ]

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

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