তথ্য দ্রুত সাজানোর জন্য বাছাই করা


11

আমাকে bedএলোমেলোভাবে 10000 বার একটি ফাইল বাছাই করতে হবে এবং প্রতিবার শীর্ষে 1000 টি সারি নেওয়া দরকার। বর্তমানে, আমি নিম্নলিখিত কোড ব্যবহার করছি:

for i in {1..100}; do
    for j in {1..100}; do
        sort -R myfile.bed_sorted | tail -n 1000 > myfile.bed.$i.$j.bed
    done
done

প্রতিটি ফাইলের জন্য এটি করতে প্রায় 6 ঘন্টা সময় লাগে। আমার প্রায় 150 টি কাজ করার দরকার আছে। এর জন্য আরও দ্রুত সমাধান কি আছে?

আমার কাছে থাকা ডেটাগুলির একটি নমুনা (মাইফাইল.বেড_সোর্টার্ড):

    chr1    111763899   111766405   peak1424    1000    .   3224.030    -1  -1
    chr1    144533459   144534584   peak1537    998 .   3219.260    -1  -1
    chr8    42149384    42151246    peak30658   998 .   3217.620    -1  -1
    chr2    70369299    70370655    peak16886   996 .   3211.600    -1  -1
    chr8    11348914    11352994    peak30334   990 .   3194.180    -1  -1
    chr21   26828820    26830352    peak19503   988 .   3187.820    -1  -1
    chr16   68789901    68791150    peak11894   988 .   3187.360    -1  -1
    chr6    11458964    11462245    peak26362   983 .   3169.750    -1  -1
    chr1    235113793   235117308   peak2894    982 .   3166.000    -1  -1
    chr6    16419968    16422194    peak26522   979 .   3158.520    -1  -1
    chr6    315344  321339  peak26159   978 .   3156.320    -1  -1
    chr1    111756584   111759633   peak1421    964 .   3110.520    -1  -1
    chrX    12995098    12997685    peak33121   961 .   3100.000    -1  -1
    chr9    37408601    37410262    peak32066   961 .   3100.000    -1  -1
    chr9    132648603   132651523   peak32810   961 .   3100.000    -1  -1
    chr8    146103178   146104943   peak31706   961 .   3100.000    -1  -1
    chr8    135611963   135614649   peak31592   961 .   3100.000    -1  -1
    chr8    128312253   128315935   peak31469   961 .   3100.000    -1  -1
    chr8    128221486   128223644   peak31465   961 .   3100.000    -1  -1
    chr8    101510621   101514237   peak31185   961 .   3100.000    -1  -1
    chr8    101504210   101508005   peak31184   961 .   3100.000    -1  -1
    chr7    8173062 8174642 peak28743   961 .   3100.000    -1  -1
    chr7    5563424 5570618 peak28669   961 .   3100.000    -1  -1
    chr7    55600455    55603724    peak29192   961 .   3100.000    -1  -1
    chr7    35767878    35770820    peak28976   961 .   3100.000    -1  -1
    chr7    28518260    28519837    peak28923   961 .   3100.000    -1  -1
    chr7    104652502   104654747   peak29684   961 .   3100.000    -1  -1
    chr6    6586316 6590136 peak26279   961 .   3100.000    -1  -1
    chr6    52362185    52364270    peak27366   961 .   3100.000    -1  -1
    chr6    407805  413348  peak26180   961 .   3100.000    -1  -1
    chr6    32936987    32941352    peak26978   961 .   3100.000    -1  -1
    chr6    226477  229964  peak26144   961 .   3100.000    -1  -1
    chr6    157017923   157020836   peak28371   961 .   3100.000    -1  -1
    chr6    137422769   137425128   peak28064   961 .   3100.000    -1  -1
    chr5    149789084   149793727   peak25705   961 .   3100.000    -1  -1
    chr5    149778033   149783125   peak25702   961 .   3100.000    -1  -1
    chr5    149183766   149185906   peak25695   961 .   3100.000    -1  -1

1
আপনার ফাইলটি কত বড় এবং আপনার "এলোমেলো" ধারণাটি কতটা কঠোর? split, ত্রুটিযুক্ত, প্রতিটি একটি ফাইলকে 1000 লাইনের টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টুকরো টানা বিভক্ত করা যায়, যাতে আপনি একক কলে আরও বেশি ফাইল পাবেন sort। এছাড়াও, আপনি পুরো ফাইলটি পড়ার প্রয়োজন নেই headবলে কিছুটা দ্রুত কিনা তা পরীক্ষা tailকরে দেখেছেন?
উলরিচ শোয়ার্জ

@ অলিরিখসওয়ার্জ: আমি উপরে যে নমুনা ফাইলটি পোস্ট করেছি তাতে প্রায় 33000 সারি রয়েছে। সাধারণভাবে, আমার সমস্ত বিছানার ফাইলগুলিতে কম বেশি কম সংখ্যক সারি থাকবে। এছাড়াও উদাহরণস্বরূপ: একটি 33000 সারি ফাইল থেকে, আমি একক দৌড়ে 33 টি সাবসেট (প্রত্যেকটিতে 1000 সারি) পেতে চাই না। আমি কেবল প্রতিটি রান থেকে শীর্ষস্থানীয় 1000 সারি নিতে চাই। আমি একই ফাইলটির একটি লেজও করব। শুধু নমুনার জন্য, আমি headএখানে ব্যবহার ।
বায়োবোধন

ম্যান পৃষ্ঠা অনুসারে sort -R"কীগুলির র্যান্ডম হ্যাশ" ব্যবহার করা হয়েছে। হ্যাশ তৈরি করা মোট সময় নষ্ট এবং সম্ভবত অন্য যে কোনও কিছুর চেয়ে বেশি সময় নেয়। অ্যারেতে লাইনগুলি পড়া এবং তারপরে সূচকগুলি ব্যবহার করে বদলানো ভাল। ব্যক্তিগতভাবে, আমি তার perlজন্য ব্যবহার করব ; আপনি এটি দিয়ে করতে পারেন bashতবে এলোমেলো সংখ্যা উত্পন্ন করতে আপনার একটি ফাংশন প্রয়োজন need
গোল্ডিলোকস

@ গোল্ডিলোকস: আমি একজন perlব্যক্তি নই ! আপনি কি আমাকে সাহায্য করতে পারেন?
biobudhan

6
shufপরিবর্তে চেষ্টা করুন sort -R, এটি যথেষ্ট দ্রুত। অবশ্যই, মেমোরিতে এটি করা (পার্ল উত্তর দেখুন) শেলটিতে পুরো ফাইলটি পুনরায় পড়ার প্রয়োজন এমন কোনও কিছুকে হারাবে।
frostschutz

উত্তর:


14

ধরে নিচ্ছি আপনার কাছে ফাইলটি স্লাপ করার মতো পর্যাপ্ত মেমরি রয়েছে, আপনি চেষ্টা করতে পারেন

perl -e 'use List::Util 'shuffle'; @k=shuffle(<>); print @k[0..999]' file.bed

যেহেতু আপনি এটি 10000 বার করতে চান, তাই আমি পুনরাবৃত্তিটি স্ক্রিপ্টের সাথে সংহত করার এবং জিনিসগুলিকে গতি বাড়ানোর জন্য অ্যারের পরিবর্তে সূচকগুলি বদলানোর পরামর্শ দেব :

$ time perl -e 'use List::Util 'shuffle'; 
            @l=<>; for $i (1..10000){
               open(my $fh, ">","file.$i.bed"); 
               @r=shuffle(0..$#l); 
               print $fh @l[@r[0..999]]
            }' file.bed

real    1m12.444s
user    1m8.536s
sys     0m3.244s

উপরোক্ত প্রতিটি ফাইলের মধ্যে 1000 লাইনের 10000 ফাইল তৈরি করেছেন যাতে 37000 টি সারি রয়েছে (আপনার উদাহরণের ফাইলটি 1000 বার পুনরাবৃত্তি করা হয়েছে)। আপনি দেখতে পাচ্ছেন, এটি আমার সিস্টেমে তিন মিনিটেরও বেশি সময় নিয়েছে।

ব্যাখ্যা

  • use List::Util 'shuffle';: এটি একটি পার্ল মডিউল আমদানি করে যা shuffle()ফাংশন সরবরাহ করে যা একটি অ্যারেকে এলোমেলো করে দেয় ।
  • @l=<>;: ইনপুট ফাইল ( <>) অ্যারেতে লোড করুন @l
  • for $i (1..10000){} : এটি 10000 বার চালান।
  • @r=shuffle(0..$#l);: $#lউপাদানের সংখ্যা @lতাই @rএখন অ্যারের সূচক সংখ্যার একটি এলোমেলোভাবে তালিকা @l(ইনপুট ফাইলের লাইন)।
  • open(my $fh, ">","file.$i.bed");: file.$i.bedলেখার জন্য ডাকা একটি ফাইল খুলুন । $i1 থেকে 10000 পর্যন্ত মান গ্রহণ করবে।
  • print $fh @l[@r[0..999]]: বদলানো অ্যারেতে প্রথম 1000 সূচকগুলি নিন এবং সংশ্লিষ্ট লাইনগুলি (এর উপাদানগুলি @l) মুদ্রণ করুন ।

আরেকটি পদ্ধতির ব্যবহার হ'লshuf ( ধন্যবাদ @ ফ্রোস্টচুটজ ):

$ time for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.abed; done

real    1m9.743s
user    0m23.732s
sys     0m31.764s

কি দারুন!! ঐটা অসাধারণ!! এটি 2 মিনিটের মধ্যে কাজ করেছে :-) আমার আরও একটি প্রশ্ন আছে। কিভাবে ফাইলের শেষ 1000 লাইন পুনরুদ্ধার সম্পর্কে? কারণ এগুলি পেতে আমাদের ফাইলের দৈর্ঘ্য (রেখার সংখ্যা) জানতে হবে? সাহায্য করুন!
বায়োবোধন

1
@biobudhan বিবেচনা করবেন shufযেমন frostschutz দ্বারা প্রস্তাবিত: for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.bed; done। এটি আমার সিস্টেমে এক মিনিট সময় নিয়েছে। শেষ 1000 লাইন হিসাবে, আপনার যা প্রয়োজন তা হল tail -n 1000
টেরডন

1
@biobudhan একটি 3x দ্রুত পার্ল সংস্করণের আপডেট হওয়া উত্তরও দেখতে পান।
টেরডন

হ্যাঁ, আমি এটি চেষ্টা করেছি এবং এটি এখন দ্রুত কাজ করে !! আপনাকে অনেক ধন্যবাদ!!! :-)
বায়োবোধন

আপনি কি পার্ল সংস্করণের আউটপুট ফাইলগুলি দ্বিগুণ পরীক্ষা করেছেন? এটি আমার কাছে অদ্ভুত বলে মনে হচ্ছে যে এর এত কম sysসময় আছে, যা ফাইল I / O হবে - shufএটি ~ 30s এর চেয়ে সম্পূর্ণ আলাদা হওয়া উচিত নয় sys। সুতরাং আমি এখানে পার্লটি পরীক্ষা করেছি (কাটা এন 'পেস্ট) এবং ও_ও এটি 1000 টি ফাইল তৈরি করেছে তবে সমস্ত ফাইল খালি ছিল ...
গোল্ডিলকস

9

এটি কত দ্রুত করা যায় তা দেখতে যদি আপনি কোনও বেঞ্চমার্ক চান তবে এটি অনুলিপি করুন 10kshuffle.cppএবং সংকলন করুন g++ 10kshuffle.cpp -o 10kshuffle। তারপরে আপনি এটি চালাতে পারেন:

10kshuffle filename < inputfile

কোথায় filenameআউটপুট ফাইলের জন্য ব্যবহার করার জন্য একটি বেস পথ; তারা নামে করা হবে filename.0, filename.1ইত্যাদি এবং প্রতিটি একটি এলোমেলো প্রথম 1000 লাইন রয়েছে। এটি প্রতিটি ফাইলের নাম যেমন লিখেছে তেমন লিখেছে।

#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>
#include <vector>

using namespace std;

unsigned int randomSeed () {
    int in = open("/dev/urandom", O_RDONLY);
    if (!in) {
        cerr << strerror(errno);
        exit(1);
    }
    unsigned int x;
    read(in, &x, sizeof(x));
    close(in);
    return x;
}

int main (int argc, const char *argv[]) {
    char basepath[1024];
    strcpy(basepath,argv[1]);
    char *pathend = &basepath[strlen(basepath)];
// Read in.
    vector<char*> data;
    data.reserve(1<<16);
    while (!cin.eof()) {
        char *buf = new char[1024];
        cin.getline(buf,1023);
        data.push_back(buf);
    }

    srand(randomSeed());
    for (int n = 0; n < 10000; n++) {
        vector<char*> copy(data);
    // Fisher-Yates shuffle.
        int last = copy.size() - 1;
        for (int i = last; i > 0; i--) {
            int r = rand() % i;
            if (r == i) continue;
            char *t = copy[i];
            copy[i] = copy[r];
            copy[r] = t;
        }
    // Write out.
        sprintf(pathend, ".%d", n);
        ofstream file(basepath);
        for (int j = 0; j < 1000; j++) file << copy[j] << endl;
        cout << basepath << endl;
        file.close();
    }

    return 0;
}  

একটি একক 3.5 গিগাহাড কোর, এটি 20 সেকেন্ডে চলে:

   time ./10kshuffle tmp/test < data.txt
   tmp/test.0
   [...]
   tmp/test.9999
   real 19.95, user 9.46, sys 9.86, RSS 39408

data.txtপ্রশ্ন থেকে নকল 37000 লাইন ছিল? আপনি যদি প্রথম 1000 লাইনের পরিবর্তে আউটপুট ফাইলে পুরো বদল চান তবে 54 টি রেখাটি পরিবর্তন করুন:

for (int j = 0; j < copy.size(); j++) file << copy[j] << endl; 

3

সুতরাং আপনার প্রশ্নের একটি ইউনিক্স দিক রয়েছে, তবে এটি প্রথমে আপনার মৌলিক সমস্যাটি সমাধান করা এবং তারপরে সমাধানটি বাস্তবায়নের জন্য ইউনিক্স-ওয়াইয়ের উপায় অনুসন্ধান করার চেষ্টা করা সার্থক।

আপনাকে অজানা, প্রচুর সংখ্যক সারি সহ একটি ফাইল থেকে প্রতিটি আকারের 10,000 টি 10,000 নমুনা তৈরি করতে হবে। আপনি যদি 10,000 ম 1000 সারি মেমরিতে ধরে রাখতে পারেন তবে ফাইলটির একক পাসে এটি করা সম্ভব । যদি আপনি এতগুলি সারি মেমোরিতে রাখতে না পারেন, তবে আপনার ফাইলটিতে কতগুলি সারি রয়েছে তা আপনি যদি জানেন তবে আপনি এটি একক পাসে করতে পারেন। আপনি যদি জানেন না যে আপনার ফাইলটিতে কতগুলি সারি রয়েছে, সারিগুলির সংখ্যা গণনা করতে আপনার একটি অতিরিক্ত পাসের প্রয়োজন।

আপনি যখন সারিগুলির সংখ্যা জানেন না তখন আরও বেশি শক্তিশালী ক্ষেত্রে অ্যালগরিদমটি প্রতিটি নমুনার জন্য নিম্নলিখিতটি করা হয় (সমান্তরালভাবে, নমুনাগুলিকে স্মৃতিতে রেখে):

  • নমুনায় প্রথম 1,000 সারি অন্তর্ভুক্ত করুন
  • এন-তম সারি (যেখানে n > 1000) এর জন্য, এটি সম্ভাবনার সাথে অন্তর্ভুক্ত করুন 1000 / nএবং আপনি ইতিমধ্যে নির্বাচিত সারিগুলি থেকে একটি এলোমেলো সারি বাতিল করুন। (কিছু সারি ফেলে দেওয়ার সম্ভাবনার কারণে আমাদের ইনপুট শেষ না হওয়া পর্যন্ত নমুনাটি মেমরিতে ধারণ করতে হবে)

দ্বিতীয় ধাপটি কার্যকর করার একটি দুর্দান্ত উপায় হ'ল একটি এলোমেলো পূর্ণসংখ্যার উত্পাদন kকরা [1, n]। তাহলে k <= 1000সারিটি অন্তর্ভুক্ত করুন এবং kএটির সাথে বিদ্যমান- তম সারিকে প্রতিস্থাপন করুন । এখানে অ্যালগরিদমের আরও মানক বিবরণ দেওয়া হয়েছে: http://en.wikedia.org/wiki/Reservoir_sampling

আপনি যদি সারিগুলির সংখ্যা জানেন R, তবে:

  • s0 এর নমুনা-আকার দিয়ে শুরু করুন
  • সম্ভাব্যতার সাথে এন-তম সারি অন্তর্ভুক্ত করুন (1000 - s) / (R - n + 1)এবং এটি অবিলম্বে আউটপুট করুন (এবং নমুনার আকার বৃদ্ধি করুন s)

ইউনিক্সে এটি কীভাবে করবেন? awkইন্টারনেটে এই পোস্টের প্রতি উত্তর বলে মনে হচ্ছে (আমি এর সঠিকতার পক্ষে কোনও প্রমাণ দিতে পারি না, তবে কোডটি এখানে রয়েছে) https://news.ycombinator.com/item?id=4840043

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.