একটি পাঠ্য ফাইলের ভিতরে নকল লাইনগুলি কীভাবে সরিয়ে ফেলা যায়?


126

মাইনের একটি বিশাল (2 GiB অবধি) পাঠ্য ফাইলটিতে এতে প্রতিটি লাইনটির প্রায় 100 টি সঠিক নকল রয়েছে (ফাইলটি CSV- এর মতো ডেটা টেবিল হিসাবে আমার ক্ষেত্রে অকেজো)।

আমার যা প্রয়োজন তা হল মূল ক্রম ক্রম বজায় রাখার সময় সমস্ত পুনরাবৃত্তি (মুছে ফেলা তবে এটি একটি তাত্পর্যপূর্ণ পারফরম্যান্স বৃদ্ধির জন্য উত্সর্গ করা যেতে পারে)। ফলাফল প্রতিটি লাইন অনন্য হতে হবে। যদি 100 টি সমান লাইন থাকে (সাধারণত ডুপ্লিকেটগুলি ফাইল জুড়ে ছড়িয়ে থাকে এবং প্রতিবেশী হবে না) তবে সেখানে কেবল এক ধরনের বাম থাকতে হবে।

আমি এটি বাস্তবায়নের জন্য স্কালায় একটি প্রোগ্রাম লিখেছি (এটি জাভা বিবেচনা করুন) তবে সম্ভবত আরও দ্রুত সি-লিখিত নেটিভ সরঞ্জামগুলি এটি করতে সক্ষম?

আপডেট: awk '!seen[$0]++' filenameসমাধানটি আমার পক্ষে ঠিক ঠিক কাজ করছে যতক্ষণ না ফাইলগুলি 2 জিআইবি বা তার চেয়ে কম ছোট ছিল তবে এখন আমি 8 গিগাবাইট ফাইলটি পরিষ্কার করতে চাইলে এটি আর কাজ করে না। মনে হয় 4 জিআইবি র‌্যামযুক্ত একটি ম্যাক এবং 4 জিবিবি র‌্যাম এবং 6 জিআইবি স্বাপের সাথে একটি 64-বিট উইন্ডোজ 7 পিসি অনন্তর গ্রহণ করছে memory এবং লিনাক্সে 4 জিবি র‌্যামের এই অভিজ্ঞতাটি দিয়ে চেষ্টা করার বিষয়ে আমি উত্সাহী বোধ করি না।


এটি আপনার ক্রমটি ধ্বংস করবে তবে, আপনি কি সাজানোর চেষ্টা করেছেন-এটি, এত বড় ফাইলটিতে কীভাবে বা চালানো যায় তা আমার কোনও ধারণা নেই
0x7c0

5
সি প্রায়শই জাভার চেয়ে তাত্পর্যপূর্ণ দ্রুত হয় না এবং আপনি যদি এখন এটি চালাচ্ছেন (ক্রম) তবে এখানে উত্তর পাওয়ার আগে এটি কার্যকর করার যথেষ্ট সুযোগ রয়েছে, এটি বাস্তবায়ন করবেন এবং এটি চলমান শেষ করবে; ক্রম বাইরে, sort -uসম্ভবত দ্রুত হবে।
কেভিন

উত্তর:


214

awk# ব্যাশ (ফ্রিনোড) এ একটি সমাধান দেখা গেছে:

awk '!seen[$0]++' filename

1
এটি কেবল একটি 2 জি ফাইলে চেষ্টা করেছেন এবং আমার নোটবুকটিতে এটি তিন মিনিট সময় নিয়েছে। খারাপ না. আমি ইউনিক ফাইলের নাম চেষ্টা করেছিলাম | awk '! দেখেছেন [$ 0] ++' তবে এটি আর দ্রুত হয়নি।
মিগজেকে

এটি awk2 টি অ্যারে লুকআপগুলি ব্যবহার করে আরও ভার্জোজ ভার্সনের চেয়ে আশ্চর্যজনকভাবে দ্রুততর (গিলস জবাবের একটি বিস্তৃত ব্যাখ্যা হিসাবে দেখানো হয়েছে): 0m36.132s বনাম 0m49.958s .. 50 মিলিয়ন লাইনের জন্য .. আমি ভেবেছিলাম যে বাধাটি I / O হবে, তবে অতিরিক্ত অ্যারের সন্ধান হ'ল ... অ্যারেতে 1 মিলিয়ন উপাদান একটি উল্লেখযোগ্য দাঁত তৈরি করেছে ...
পিটার.ও

তবে কীভাবে তা সাজান-ইউ এর সাথে তুলনা করে ....?
হ্যাশ উইজার্ড

1
@ হ্যাশ উইজার্ড: এই আদেশটি বাছাই করে না, তবে একই লাইনের প্রতিটি পরবর্তী ঘটনা সরিয়ে দেয়
এনজোটিব

1
@ ম্যাক্সউইলিয়ামস হ্যাঁ, এটি কাজ করে যে এলোমেলোভাবে বিতরণ করা হয়েছে।
সেথোলোপলাস

47

স্ট্যান্ডার্ড ইউটিলিটিগুলি ব্যবহার করে একটি সহজ (যা স্পষ্ট করে বলা যায় না) পদ্ধতি রয়েছে যা চালানো ছাড়া বড় মেমরির প্রয়োজন হয় না sort, যা বেশিরভাগ বাস্তবায়নে বিশাল ফাইলগুলির জন্য নির্দিষ্ট অপ্টিমাইজেশন রয়েছে (একটি ভাল বাহ্যিক সাজানোর অ্যালগরিদম)। এই পদ্ধতির একটি সুবিধা হ'ল এটি কেবল বিশেষ-উদ্দেশ্যমূলক ইউটিলিটিগুলির ভিতরে সমস্ত লাইনকেই লুপ করে, কখনও কখনও ব্যাখ্যা করা ভাষার ভিতরে।

<input nl -b a -s : |           # number the lines
sort -t : -k 2 -u |             # sort and uniquify ignoring the line numbers
sort -t : -k 1n |               # sort according to the line numbers
cut -d : -f 2- >output          # remove the line numbers

যদি সমস্ত লাইন একটি শ্বেতস্থান অক্ষর দিয়ে শুরু হয়, আপনি কয়েকটি বিকল্প দিয়ে দিতে পারেন:

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output

বিপুল পরিমাণে সদৃশতার জন্য, এমন একটি পদ্ধতির জন্য যা কেবল মেমরিতে প্রতিটি লাইনের একক অনুলিপি সঞ্চয় করতে পারে তা আরও ভাল সম্পাদন করবে। কিছুটা ওভারহেডের ব্যাখ্যা সহ, এর জন্য একটি খুব সংক্ষিপ্ত অজ স্ক্রিপ্ট রয়েছে (ইতিমধ্যে এনজোটিব পোস্ট করেছেন ):

<input awk '!seen[$0]++'

কম সংক্ষিপ্তভাবে: !seen[$0] {print} {seen[$0] += 1}যেমন, বর্তমান লাইনটি এখনও না দেখা গেলে মুদ্রণ করুন, তারপরে seenএই লাইনের জন্য কাউন্টারটি বাড়িয়ে নিন (অবিচ্ছিন্ন ভেরিয়েবল বা অ্যারের উপাদানগুলির সংখ্যাগত মান 0)।

দীর্ঘ লাইনের জন্য, আপনি প্রতিটি লাইনের একটি অ-স্পফযোগ্য চেকসাম (যেমন একটি ক্রিপ্টোগ্রাফিক ডাইজেস্ট) রেখে স্মৃতি সঞ্চয় করতে পারেন। উদাহরণস্বরূপ, SHA-1 ব্যবহার করে আপনার প্রতি লাইনে কেবল 20 বাইট এবং একটি ধ্রুবক ওভারহেডের প্রয়োজন। তবে গণনা হজম বরং ধীর; এই পদ্ধতিটি কেবল তখনই জিততে পারে যদি আপনার কাছে একটি দ্রুত সিপিইউ থাকে (বিশেষত একটি ডাইজেটস গণনা করার জন্য একটি হার্ডওয়ার এক্সিলার সহ একটি) এবং ফাইলের আকারের এবং যথেষ্ট দীর্ঘ লাইনের সাথে তুলনায় অনেক বেশি মেমরি নয়। কোনও মৌলিক ইউটিলিটি আপনাকে প্রতিটি লাইনের জন্য একটি চেকসাম গণনা করতে দেয় না; আপনাকে পার্ল / পাইথন / রুবি /… এর ব্যাখ্যা ওভারহেড বহন করতে হবে বা একটি উত্সর্গীকৃত সংকলিত প্রোগ্রাম লিখতে হবে।

<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output

@ গিলস আপনার ব্যাখ্যার উপর ভিত্তি করে awk '!seen[$0]++', এর অর্থ কি এই যে যদি খসড়া 2 টি সদৃশ লাইন দেখে তবে এটি সর্বদা প্রথমটিকে ধরে রাখবে এবং পরবর্তী সমস্তগুলি উপেক্ষা করবে? (বা এটি
সর্বশেষটি রাখবে

1
@ user779159 এটি প্রথমটি রাখে: প্রতিটি ইনপুট লাইনটি তাত্ক্ষণিকভাবে প্রিন্ট করা হয় (প্রথম ঘটনা) অথবা মোটেও নয় (পুনরাবৃত্ত ঘটনা)।
গিলস

তবে কীভাবে তা সাজানোর সাথে তুলনা করা যায় ...?
হ্যাশ উইজার্ড

@ হ্যাশ উইজার্ড একটি সমতল আদেশ sort -uপরিবর্তন করে। আমার উত্তরটি এমন সমাধানগুলি দেখায় যা অর্ডার সংরক্ষণ করে (প্রথম সংক্রমণের ক্রম, সুনির্দিষ্ট হওয়ার জন্য)।
গিলস

@ গিলস আপনি কি বলবেন যে এটি 50% নকলের সাথে বড় ফাইলগুলির জন্য (10 জি) বাছাই করার চেয়ে দ্রুত?
হ্যাশ উইজার্ড

25
sort -u big-csv-file.csv > duplicates-removed.csv

নোট করুন আউটপুট ফাইল বাছাই করা হবে।


1
awkঅন্যান্য উত্তরে কমান্ডের মতো দ্রুত নয় , তবে ধারণাটি সহজ!
জোহান

@ জোহান আমি প্রায় হাজার হাজার (এমনকি মিলিয়ন) সংক্ষিপ্ত নিউলাইন টার্মিনেটেড স্ট্রিং সহ ফাইলগুলিতে এটি প্রায়শই করছি। আমি যে পরীক্ষাগুলি করছি তার জন্য ফলাফলগুলি বেশ দ্রুত পেয়েছি। এটি আরও গুরুত্বপূর্ণ হতে পারে যদি স্ক্রিপ্টগুলিতে ব্যবহৃত হয় যা বারবার চালিত হয়, সময় সাশ্রয় বিবেচনাযোগ্য হতে পারে।
ভ্লাদিস্লাভস ডভগ্লেলেকস

1
sort -uবাছাইয়ের সময় সদৃশ হওয়ার সময় সদৃশগুলি সরিয়ে ফেলতে ব্যবহার করুন । (এবং মেমোরি ব্যান্ডউইথ সংরক্ষণ করে) এটি অন্য প্রোগ্রামে পাইপ করে)। awkআপনি যদি নিজের আউটপুটও বাছাই করতে চান তবে এটি সংস্করণটির চেয়ে ভাল । (এই প্রশ্নের ওপি তার মূল আদেশটি সংরক্ষণ করতে চায় , তাই কিছুটা ভিন্ন ব্যবহারের ক্ষেত্রে এটি একটি উত্তরের উত্তর))
পিটার কর্ডেস

আমার জন্য, 5.5 মিলিয়ন লাইন ফাইলের জন্য (মোট 1.8 গিগাবাইট) প্রায় এক মিনিট সময় নিয়েছিল। উজ্জ্বল।
ম্যাক্স উইলিয়ামস

18

ধরে নিই যে আপনি ডি-ডুপ্লিকেট ফাইলটিকে মেমরিতে রাখার মতো সামর্থ্য রাখতে পারবেন (যদি আপনার ডেটাটি যদি সত্যই 100 এর ফ্যাক্টর দ্বারা নকল হয়, তবে এটি প্রায় 20MiB + ওভারহেড হওয়া উচিত) আপনি পার্লের সাহায্যে খুব সহজেই এটি করতে পারেন।

$ perl -ne 'print unless $dup{$_}++;' input_file > output_file

এটি অর্ডারও সংরক্ষণ করে।

আপনি চাইলে প্রতিটি লাইনের সংখ্যার সংখ্যা বের %dupকরতে পারেন, যদি আপনি চান তবে একটি অতিরিক্ত বিনামূল্যে বোনাস হিসাবে।

আপনি যদি পছন্দ করেন awkতবে এটিও করা উচিত (পার্ল সংস্করণ হিসাবে একই যুক্তি, একই ক্রম, একই ডেটা dupভেরিয়েবলে সংগৃহীত ):

$ awk '{if (++dup[$0] == 1) print $0;}' input_file > output_file

এটি @ ম্যাট খুব ভাল, আমি ফাইলটি স্লারপ করতে চলেছি, এলও ;-)।
নিখিল মুলি

এখন @ মনআটওয়ার্কের জন্য তার এসড এবং অদ্ভুত ম্যাজিক ওয়েভরির জন্যও অপেক্ষা করা হচ্ছে :-)
নিখিল

অজানা টিপটির জন্য আবার দুর্দান্ত :-)
নিখিল মুলি

1
শুধুমাত্র ডুপ্লিকেট সংলগ্ন লাইনগুলি সরাতে পার্ল স্ক্রিপ্টটি পরিবর্তন করা সম্ভব?
ডাম্বলডেড

2
@ ডাম্বলড্যাড: এগুলি uniqনিজেই করে
ম্যাট

3

অন্য কোনও উত্তর অন্তর্ভুক্ত সমর্থন হিসাবে সরবরাহ করা হয়নি, এখানে এক:

gawk -i inplace '!a[$0]++' file

এটি কি অর্ডার সংরক্ষণ করে? যাইহোক, এটি আমার পক্ষে কার্যকর হয়নি। আমার সংস্করণটি হ'ল:GNU Awk 4.0.2
লিওনিড

1
@ লিওনিড হ্যাঁ, এটি করে। এটি কোনও অনন্য লাইনের প্রথম উপস্থিতি মুদ্রণ করে। Inplace সমর্থন প্রথম সংস্করণ 4.1, যা 2013 সালে মুক্তি পায় চালু হয়
rindeal - জানুয়ারী Chren

3

আপনি http://www.computerhope.com/unix/uuniq.htm ব্যবহার করতে পারেনuniq

uniq ফাইলের পুনরাবৃত্ত লাইনগুলি রিপোর্ট করে বা ফিল্টার করে।


উত্তর দেওয়ার সময় আপনার উত্তরটি হ'ল কেন তা সম্পর্কে কিছুটা ব্যাখ্যা দেওয়া ভাল । সুতরাং, পূর্ববর্তী উত্তরগুলির মধ্যে এই উত্তরটি কীভাবে আলাদা?
স্টিফেন রাউচ

1
ইউনিট ম্যান পৃষ্ঠা থেকে: দ্রষ্টব্য: 'uniq' does not detect repeated lines unless they are adjacent. সুতরাং আপনাকে প্রথমে এটি বাছাই করতে হবে এবং নন-অনুলিপি লাইনের ক্রমটি আলগা করতে হবে।
Vindolin

2

পাইথন ওয়ান লাইনারস:

python -c "import sys; lines = sys.stdin.readlines(); print ''.join(sorted(set(lines)))" < InputFile

এর ফলে পুরো ফাইলটি মেমোরিতে স্লাপ হয়ে যায় এবং ওপি'র সমস্যার জন্য এটি উপযুক্ত উপযুক্ত নাও হতে পারে। এছাড়াও অর্ডার ধরে রাখা নিশ্চিত নয়
Iruvar

এই পরামর্শের জন্য ধন্যবাদ, আমি কেবল অজগর শিখছি .. শেখার উদ্দেশ্যে এটি চেষ্টা করেছি .. :)
রাহুল পাতিল

এখানে একটি পাইথন ২.7 সংস্করণ যা ওয়ান-লাইনার নয় তবে (সংক্ষেপে) পুরো ফাইলটিকে মেমরিতে লোড না করা বা মুদ্রণের জন্য একক
বিশালাকার

ধন্যবাদ @ 1_CR আমার আজ কিছু শিখতে হবে :)OrderedDict
রাহুল পাতিল

0

এগুলির কোনও উত্তর আমার ম্যাকের জন্য আমার পক্ষে কাজ করেনি তাই আমি একটি সাধারণ অজগর স্ক্রিপ্ট লিখেছিলাম যা আমার পক্ষে কাজ করে। আমি শীর্ষস্থানীয় / পশ্চাদপদ সাদা স্থানটিকে উপেক্ষা করছি এবং মেমরির খরচ সম্পর্কেও চিন্তা করি না।

import sys

inputfile = sys.argv[1]
outputfile = sys.argv[2]

with open(inputfile) as f:
    content = f.readlines()

content = [x.strip() for x in content]

my_list = list(set(content))

with open(outputfile, 'w') as output:
    for item in my_list:
        output.write("%s\n" % item)

উপরেরটি ইউনিক.পি তে সংরক্ষণ করুন এবং এটি চালান:

python unique.py inputfile.txt outputfile.txt

-1

ব্যাশ 4 এর সাথে, একটি খাঁটি-ব্যাশ সমাধান যা এসোসিয়েটিভ অ্যারেগুলির সুবিধা নেয় তা ব্যবহার করা যেতে পারে। এখানে একটি উদাহরণ

unset llist; declare -A llist;
while read -r line; do
if [[ ${llist[$line]} ]]; then
  continue
else 
  printf '%s\n' "$line"
  llist[$line]="x"
fi
done < file.txt

2
readবড় টেক্সট ফাইলগুলি প্রক্রিয়া করতে লুপগুলি ব্যবহার করবেন না । কোনও নতুন লাইনের ওভারশুটিং এড়াতে বাশকে একবারে বাই-বাইট-পড়তে হবে। অ্যাশকের তুলনায় টেক্সট প্রসেসিংয়ে বাশ খুব দ্রুত নয় not আপনি যদি এটি ব্যবহার read -raকরেন তবে আপনার ইনপুটটিতে ব্যাকস্ল্যাশ খাওয়া এড়ানো হবে। এছাড়াও, লুপটি unset llist পরে ভুলে যাবেন না , যদি আপনি এটি শেল ফাংশনটিতে রাখেন বা এটি ইন্টারেক্টিভভাবে ব্যবহার করেন।
পিটার কর্ডেস

2
@PeterCordes, অথবা আপনি শুধু রেফারেন্সড পারতেন এই :-)
Iruvar
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.