سؤال

لدي ملفان A-nodes_to_delete و B-nodes_to_keep. يحتوي كل ملف على العديد من الخطوط ذات المعرفات الرقمية.

أريد أن أحصل على قائمة المعرفات الرقمية الموجودة nodes_to_delete ولكن ليس في nodes_to_keep, ، على سبيل المثال alt text .

القيام بذلك ضمن قاعدة بيانات postgreSQL بطيئة بشكل غير معقول. أي طريقة أنيقة للقيام بذلك في باش باستخدام أدوات Linux CLI؟

تحديث: يبدو أن هذا عمل بيثوني ، لكن الملفات كبيرة حقًا. لقد حلت بعض المشكلات المماثلة باستخدام uniq, sort وبعض تقنيات نظرية مجموعة. كان هذا حوالي اثنين أو ثلاثة أوامر من حجم أسرع من معادل قاعدة البيانات.

هل كانت مفيدة؟

المحلول

ال بالاتصالات القيادة تفعل ذلك.

نصائح أخرى

لقد أوضح لي أحدهم كيف أفعل هذا بالضبط منذ شهرين ، وبعد ذلك لم أتمكن من العثور عليه لفترة من الوقت ... وبينما كنت أبحث عن سؤالك. ها هو :

set_union () {
   sort $1 $2 | uniq
}

set_difference () {
   sort $1 $2 $2 | uniq -u
}

set_symmetric_difference() {
   sort $1 $2 | uniq -u
}

يستخدم comm - سيقارن خط ملفين مصنفين سطرا.

الجواب القصير على سؤالك

سيعيد هذا الأمر خطوطًا فريدة من نوعها إلى Deletenodes ، ولكن ليس الخطوط في keepnodes.

comm -1 -3 <(sort keepNodes) <(sort deleteNodes)

مثال الإعداد

دعنا ننشئ الملفات المسماة keepNodes و deleteNodes, ، واستخدامها كمدخلات غير مصورة لـ comm أمر.

$ cat > keepNodes <(echo bob; echo amber;)
$ cat > deleteNodes <(echo bob; echo ann;)

بشكل افتراضي ، يطبع تشغيل Comm بدون وسيطات 3 أعمدة مع هذا التصميم:

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both

باستخدام ملفات المثال لدينا أعلاه ، قم بتشغيل Comm بدون وسيطات. لاحظ الأعمدة الثلاثة.

$ comm <(sort keepNodes) <(sort deleteNodes)
amber
    ann
        bob

قمع إخراج العمود

قمع العمود 1 أو 2 أو 3 مع -n ؛ لاحظ أنه عند إخفاء العمود ، يتقلص المسافة البيضاء.

$ comm -1 <(sort keepNodes) <(sort deleteNodes)
ann
    bob
$ comm -2 <(sort keepNodes) <(sort deleteNodes)
amber
    bob
$ comm -3 <(sort keepNodes) <(sort deleteNodes)
amber
    ann
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
ann
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes)
amber
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes)
bob

الفرز مهم!

إذا قمت بتنفيذ Comm دون فرز الملف أولاً ، فإنه يفشل بأمان مع رسالة حول الملف الذي لم يتم فرزه.

comm: file 1 is not in sorted order

comm تم تصميمه خصيصًا لهذا النوع من حالات الاستخدام ، لكنه يتطلب إدخالًا مرتبة.

awk يمكن القول أنه أداة أفضل لهذا لأنها مستقيمة إلى الأمام إلى حد ما للعثور على اختلاف محدد ، لا يتطلب ذلك sort, ، ويوفر مرونة إضافية.

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete

ربما ، على سبيل المثال ، ترغب فقط في العثور على الفرق في الأسطر التي تمثل أرقامًا غير سالبة:

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
    a[$0]
    next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete

ربما تحتاج إلى طريقة أفضل للقيام بذلك في Postgres ، يمكنني أن أراهن تمامًا على أنك لن تجد طريقة أسرع للقيام بذلك باستخدام ملفات مسطحة. يجب أن تكون قادرًا على القيام بانضمام داخلي بسيط وافتراض أن كلا من كولو المعرفات مفهرسة يجب أن تكون سريعة للغاية.

لذلك ، هذا يختلف قليلاً عن الإجابات الأخرى. لا أستطيع أن أقول إن برنامج التحويل البرمجي C ++ هو بالضبط "أداة Linux CLI" ، ولكن التشغيل g++ -O3 -march=native -o set_diff main.cpp (مع الكود أدناه في main.cpp يمكن أن تفعل الخدعة):

#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
    ifstream keep_file(argv[1]), del_file(argv[2]);
    unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
    string line;
    while (getline(del_file, line)) {
        init_lines.erase(line);
    }
    copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}

للاستخدام ، ما عليك سوى تشغيل set_diff B A (ليس A B, ، حيث B هو nodes_to_keep) وسيتم طباعة الفرق الناتج إلى stdout.

لاحظ أنني غفرت بعض أفضل الممارسات C ++ للحفاظ على الكود أكثر بساطة.

يمكن إجراء العديد من التحسينات الإضافية للسرعة (بسعر المزيد من الذاكرة). mmap سيكون أيضًا مفيدًا بشكل خاص لمجموعات البيانات الكبيرة ، لكن ذلك سيجعل الكود أكثر مشاركة.

منذ أن ذكرت أن مجموعات البيانات كبيرة ، اعتقدت أن القراءة nodes_to_delete قد يكون الخط في وقت واحد فكرة جيدة لتقليل استهلاك الذاكرة. النهج المتبع في الكود أعلاه غير فعال بشكل خاص إذا كان هناك الكثير من الخداع في nodes_to_delete. أيضا ، لا يتم الحفاظ على النظام.


شيء أسهل للنسخ واللصق في bash (أي تخطي إنشاء main.cpp):

g++ -O3 -march=native -xc++ -o set_diff - <<EOF
#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
        ifstream keep_file(argv[1]), del_file(argv[2]);
        unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
        string line;
        while (getline(del_file, line)) {
                init_lines.erase(line);
        }
        copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}
EOF
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top