У меня есть два набора данных с миллионами строк. Пример выглядит так:
Набор данных 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 в нее не попадает.
Спасибо @ BogumiłKamiński за комментарий, но как зафиксировать допуск по дате? также существует ли эквивалент merge_asof () Julia, который находится в python?
Как я предложил, если у вас действительно большие данные, которые не помещаются в памяти, используйте цикл while и перемещайте курсор по обеим таблицам, отслеживая начало и конец двухдневного окна в каждой таблице.
@ 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)
Наверное, было бы хорошо, если бы вы разместили отдельный вопрос об этой ошибке с минимально воспроизводимым кодом, так как сейчас ее сложно диагностировать.




Итак, у Джулии есть 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 - хорошее решение, только если данных мало (и из описания проблемы я понял, что данные не помещаются в ОЗУ).
Что касается построчного комментария, это правда, и я сказал об этом. Даже если логика сравнения более сложна, предварительное выделение, вероятно, будет полезным, поскольку накладные расходы на добавление новых строк могут легко превысить логику сравнения / col3. Более того, DataFrames действительно исчерпывает память, правда. Но пара миллионов здесь и там могут легко поместиться в большую часть оперативной памяти, даже на ноутбуках. Но, принимая во внимание, JuliaDB был бы предпочтительнее.
Ваша основная проблема - логическое сравнение с датами. Я предлагаю вам преобразовать даты в юлианский формат даты. Вот код от 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}]
]
После того, как вы превратили дату в целое число, вам будет намного проще проводить сравнение в любом случае.
Не совсем понятно, какие у вас ограничения. Но если вы можете отсортировать набор данных 1 и набор данных 2 на
col1иcol2(в Julia вы могли бы сделать это, например, используя JuliaDB, если данные слишком велики, чтобы поместиться в ОЗУ), тогда напишите цикл, повторяющий оба набора для создания последовательных групп. Для этого нет необходимости загружать целые наборы данных в ОЗУ или сохранять результат в ОЗУ.