- ECB की eurofxref-hist.zip एक साधारण exchange-rate CSV bundle है, लेकिन सिर्फ
curl, gunzip, sqlite3 से वह तारीख 2000-10-26 तुरंत निकाली जा सकती है जब dollar, euro के मुकाबले सबसे मजबूत था
- मूल डेटा wide format में है, जहाँ
Date के बाद हर currency के लिए अलग-अलग column आते हैं; analysis के लिए यह असुविधाजनक है, इसलिए इसे Date,Currency,Rate जैसे long format में बदलकर साफ़ करना पड़ता है
- हर line के अंत में मौजूद trailing comma की वजह से CSV parser एक खाली column पढ़ लेता है, और Pandas में
.iloc[:,:-1] से आखिरी column हटाने पर melt का result साफ़ मिलता है
- साफ़ किए गए CSV को csvbase पर HTTP PUT से upload करने के बाद
gnuplot, DuckDB, sqlite3 जैसे tools से जोड़कर graph बनाने, moving average calculate करने और HTTP CSV loading में इस्तेमाल किया जा सकता है
- access negotiation, authentication, quota, जटिल API docs के बिना मिलने वाला public data open API जैसा काम करता है, और एक साधारण zip file भी financial applications के data exchange की बुनियाद बन सकती है
एक zip file से exchange-rate query करना
- ECB, euro और दूसरी currencies के historical exchange-rate data को official zip file के रूप में public करता है
- नीचे दी गई pipeline data download करती है, उसे decompress करती है, SQLite in-memory DB में CSV पढ़ती है, USD value के आधार पर sort करती है और पहली तारीख निकालती है
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip \
| gunzip \
| sqlite3 ':memory:' '.import /dev/stdin stdin' \
"select Date from stdin order by USD asc limit 1;"
- output
2000-10-26 है
curl -s standard error में आने वाला noise कम करता है, और gunzip zip file को decompress करता है
- Mac OS या BSD में BSD-family
gunzip zip files को support नहीं करता, इसलिए इसकी जगह bsdtar -xOf - इस्तेमाल करना चाहिए
sqlite3 ':memory:' in-memory DB का इस्तेमाल करता है, और .import /dev/stdin stdin standard input को stdin table में load करता है
CSV format साफ़ करना और Pandas melt
- मूल CSV header
Date,USD,JPY,BGN,CYP,CZK,DKK,... जैसा है, जहाँ date column के बाद currencies के लिए अलग-अलग columns आते हैं; यह wide format है
- filter और aggregation के लिए
Date,Currency,Rate वाला long format संभालना आसान है
- wide format को long format में बदलने के काम को आम तौर पर melt कहा जाता है
- ज़्यादातर SQL databases में melt जैसा operation नहीं होता, इसलिए data cleaning में Pandas उपयोगी है
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).melt("Date").to_csv(sys.stdout, index=False)'
- ECB file में हर line के अंत में trailing comma है, जिससे CSV parser आखिर में एक extra empty column पढ़ता है
- यह empty column
melt result के अंत में बेकार rows बनाता है, इसलिए इसे हटाना ज़रूरी है
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).iloc[:, :-1].melt("Date")\
.to_csv(sys.stdout, index=False)'
.iloc[:, :-1] सभी rows और आखिरी column को छोड़कर बाकी सभी columns चुनता है
- ECB foreign-exchange data में format cleanup की ज़रूरत है, लेकिन access negotiation, payment, sales representative से बात, email·company name·job title submit करना, quota, authentication या API docs पढ़े बिना इसे तुरंत इस्तेमाल किया जा सकता है
- सिर्फ basic format और shape issues संभालने होते हैं, इसलिए public data releases में यह अपेक्षाकृत अच्छा है
साफ़ किया हुआ data csvbase पर upload करना
- साफ़ किया गया CSV csvbase table पर upload किया जा सकता है ताकि बार-बार cleanup करने से बचा जा सके
- मौजूदा pipeline के अंत में एक और
curl जोड़ने पर CSV को HTTP PUT से upload किया जा सकता है
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).iloc[:, :-1].melt("Date")\
.to_csv(sys.stdout, index=False)' | \
curl -n --upload-file - \
'https://csvbase.com/calpaterson/eurofxref-hist?public=yes'
--upload-file - standard input से मिले data को specified URL पर upload करता है
- अगर csvbase में table नहीं है तो नया बनाता है, और अगर है तो उस table में data डालता है
-n, ~/.netrc में मौजूद authentication credentials का इस्तेमाल करता है
gnuplot से exchange-rate graph बनाना
- साफ़ की गई csvbase table को
curl से CSV के रूप में लेकर grep, cut, gnuplot से connect किया जा सकता है
curl -s https://csvbase.com/calpaterson/eurofxref-hist | \
grep USD | \
cut -d, -f 2,4 | \
gnuplot -e "set datafile separator ','; set term dumb; \
plot '-' using 1:2 with lines title 'usd'"
- यह command 6,000 से ज़्यादा data points को 80x25 character terminal में ASCII art के रूप में कुछ हद तक readable बनाकर plot करती है
gnuplot settings CSV input लेकर date और exchange rate को line graph के रूप में plot करने के लिए set हैं
set datafile separator ',': बताता है कि input CSV है
set term dumb: ASCII art में draw करता है
plot -: standard input से data लेता है
using 1:2 with lines: column 1 और 2, यानी date और exchange rate से line बनाता है
title 'usd': line का नाम usd set करता है
- SVG image के रूप में भी output किया जा सकता है; time-series data जैसा दिखाने के लिए x-axis को time बताना और time format व x-axis tick rotation set करना होगा
- बार-बार इस्तेमाल के लिए इसे
plot_timeseries_to_svg Bash function में wrap किया जा सकता है
DuckDB से moving average calculate करना
- USD exchange rate की trendline देखने के लिए DuckDB से moving average calculate किया जा सकता है
curl -s https://csvbase.com/calpaterson/eurofxref-hist | \
duckdb -csv -c "select Date, avg(value) over \
(order by date rows between 100 preceding and current row) \
as rolling from read_csv_auto('/dev/stdin')
where variable = 'USD';" | \
plot_timeseries_to_svg rolling
- अगर
duckdb नहीं है तो यही query sqlite3 के लिए बदलना भी मुश्किल नहीं है
- DuckDB, SQLite जैसा है, लेकिन row-oriented नहीं बल्कि column-oriented है
- DuckDB HTTP से CSV सीधे पढ़कर table file बना सकता है
CREATE TABLE eurofxref_hist AS SELECT * FROM
read_csv_auto("https://csvbase.com/calpaterson/eurofxref-hist");
- DuckDB type inference काफी अच्छी तरह करता है, और terminal size detect करके बड़े results को default रूप से छोटा करके दिखाता है
- बड़े queries में progress bar दिखा सकता है, और Markdown table output भी कर सकता है
public data कैसे open API जैसा काम करता है
- zip file के अंदर CSV और
brew install या apt install से आसानी से install होने वाले tools से भी बहुत सारे काम किए जा सकते हैं
eurofxref-hist.zip संगठनों के बीच data exchange protocol के रूप में बहुत simple format है
- यह zip file छोटी दिखती है, लेकिन कई financial applications इसे रोज़ इस्तेमाल करते हैं
- ECB trailing comma को वैसे ही छोड़ता है, इसकी वजह यह मानी जा सकती है कि अगर अभी उसे हटाया गया तो बहुत सा code टूट सकता है
- अगर public data बहुत आसानी से उपलब्ध कराया जाए, तो वह open API की भूमिका भी निभाता है
- अगर कई APIs remote function call से ज़्यादा data exchange जैसी हैं, तो आसानी से download होने वाले public data से उनका functional अंतर बहुत बड़ा नहीं है
csvbase के simple URL और HTTP verbs
- csvbase हर table के लिए एक URL रखता है
https://csvbase.com/<username>/<table_name>
https://csvbase.com/calpaterson/eurofxref-hist
- हर URL में चार मुख्य HTTP verbs हैं
GET: CSV प्राप्त करता है; browser में web page भी मिल सकता है
PUT: नए CSV से नई table बनाता है या existing table overwrite करता है
POST: existing table में CSV rows bulk में जोड़ता है
DELETE: उस table को delete करता है
- authentication में HTTP Basic Auth इस्तेमाल होता है
data cleaning और pipelines पर notes
- SQL databases में melt जैसी functionality देने वालों में Snowflake का UNPIVOT और MS SQL Server का PIVOT/UNPIVOT शामिल हैं
- R और Pandas के इस्तेमाल की एक अहम वजह यह है कि उनकी data cleaning capabilities मजबूत हैं
- Bash pipeline multiprocess के रूप में काम करती है, जहाँ हर program अलग process में parallel चलता है
- अक्टूबर 2000 में euro के मुकाबले dollar exchange rate
0.8252 था, जिसका मतलब था कि 1 dollar से 1.21 euro खरीदे जा सकते थे
- euro जनवरी 1999 में notes और coins के बिना launch हुआ था; शुरुआत में यह केवल banks के अंदर मौजूद था, और notes व coins बाद में आए
1 टिप्पणियां
Hacker News टिप्पणियाँ
करीब 15 साल पहले ECB में काम करते समय मुझे यह फ़ाइल याद है
यह फ़ाइल ECB वेबसाइट से अब तक सबसे ज़्यादा डाउनलोड की जाने वाली फ़ाइल थी, और कई लोग व financial institutions इसे रोज़ डाउनलोड करके अपने सिस्टम अपडेट करने में इस्तेमाल करते थे
हर दिन तय प्रकाशन समय के तुरंत बाद कुछ मिनटों के लिए traffic बहुत बढ़ जाता था, और unzip करने पर यह एक साधारण CSV file बन जाए—यह एक जानबूझकर लिया गया निर्णय था
इससे फ़ाइल को कम resources में स्थिर और तेज़ तरीके से serve किया जा सका, और उस समय ECB की public website संभालने वाली छोटी team को इस डेटा को एक single static file के रूप में देने के technical decision पर सचमुच गर्व हो सकता था
न यह flashy है, न कोई framework है
करीब 15 साल पहले, एक पुरानी बड़ी enterprise company में—जिसके products शायद किसी ने न किसी ने खरीदे होंगे—मैंने product record system और mergers/acquisitions से बचे हुए sub/parallel systems के बीच data exchange संभाला था; ज़्यादातर काम fixed-width files या delimited files को SFTP servers के जरिए भेजने-लेने वाले bulk import/export का था
उस समय product पहले ही 15 साल पुराना था, और ऐसे data sources या exports करीब 20–30 आते-जाते थे, लेकिन सब बहुत अच्छे से चलता था
आज भी शायद बिना बड़े बदलाव के इस्तेमाल हो रहा होगा, और उस समय frontend को Smalltalk वाले पुराने version से नया लिखा जा रहा था
हमारे इस्तेमाल किए गए data sources में यह सबसे आसान था
architect कहेगा कि ZIP इस purpose की specification के हिसाब से सही format नहीं है, compliance कहेगा कि personal data leakage की जाँच चाहिए, और risk team कहेगी कि malicious actors को फ़ाइल डाउनलोड करने से रोकना होगा
web प्रभारी शायद कहेगा कि site में कुछ जोड़ने के लिए approved change process चाहिए
साधारण file download और CSV files बेहतरीन हैं
काश और जगहें data को ऐसे simple formats में publish करें, और अमेरिकी government data download में जब भी “shopping cart” भरना पड़ता है तो अंदर से थोड़ा-थोड़ा मरने जैसा लगता है
इस specific pipeline को आसान बनाने वाले wrapper tools भी बहुत हैं, और अगर web view व थोड़ा advanced features चाहिए हों तो Datasette जैसी चीज़ भी अच्छी है
ZIP file को stream के रूप में पढ़कर CSV को line by line process करके transform करें, फिर Postgres में COPY FROM stdin का इस्तेमाल करके database में load किया जा सकता है
यह इतना logical और useful लगता है, फिर भी अब तक मुझे इसका पता नहीं चला था
CSV में reports बहुत हैं, इसलिए जल्दी से queries तेज़ी से चलाने के लिए इसे आज़माना चाहता हूँ
उदाहरण के लिए
"Look, this contains \"quotes\"!",012345और"Look, this contains ""quotes""!",012345जैसे quote handling के तरीके अलग हो जाते हैं, और और भी टूटे हुए उदाहरण के रूप में"Look, this contains "quotes"!",012345याLook, this contains "quotes"!,012345भी आ सकते हैंspreadsheet के निशान के रूप में
"Look, this contains ""quotes""!",12345की तरह शुरुआत का 0 कट भी सकता हैसैद्धांतिक रूप से JSON को भी हाथ से edit करके आधा-टूटा file बनाया जा सकता है, लेकिन असल में JSON files के साथ ऐसा होते मैंने शायद ही देखा है, और serial number जैसी values भी JSON में आम तौर पर “helpful” apps द्वारा आगे का 0 काट दिए जाने वाले integers नहीं, बल्कि strings ही रहती हैं
आखिर ऐसा क्यों है, क्या कोई वाजिब वजह है?
CSV को ZIP में bundled JSON document से बदल दें, तो भी फायदे वही रहेंगे
असली समस्या यह है कि statically served एक file को simple तरीके से download करने के रास्ते में बहुत ज़्यादा रुकावटें रखी गई हैं
मैंने एक government agency के लिए API बनाया था, जहाँ data साल में एक बार बदलता था या बहुत ही कम संशोधित होता था
पूरा dataset 1MB से कम की एक ZIP file में bundled किया जा सकता था, लेकिन solution architect ने requirements तय करते-करते काम बड़ा कर दिया
इस वजह से cache इस्तेमाल नहीं करने दिया गया कि जिस exact moment पर request आ रही है, उसी समय data बदल गया हो सकता है; नतीजा एक slow API बना, और data changes की सूचना subscribers को देने के लिए जरूरत से ज़्यादा complex webhook system भी बन गया
एक ZIP file शायद बहुत simple होती, लेकिन असल जरूरत से बहुत अलग भी नहीं थी
इसे और fancy बनाना हो तो file बदलने पर trigger होने वाला webhook जोड़ दें, ताकि client को रोज़ एक बार polling करने के बजाय पता चल जाए कि फिर से कब download करना है
या बदलाव होने पर pre-defined email को mailing list पर भेजने वाली एक script बना देना भी काफी है
अगर पिछली बार से कुछ नहीं बदला है तो खाली HTTP 304 response मिलेगा, और अगर बदला है तो नए ETag के साथ 1MB से कम की ZIP file फिर से मिल जाएगी; समझ नहीं आता इसमें क्या कमी है
cache complexity बढ़ाता है और cache को manually revalidate करने का risk भी पैदा करता है, इसलिए हो सकता है solution architect सही था
अगर एक नतीजा
2000-10-26पाने के लिए 565KB की फ़ाइल डाउनलोड करनी पड़े, तो यह एक भयानक API हैअगर आप बहुत सारा डेटा लाकर यूज़र को फिर से उपलब्ध कराना चाहते हैं, तो ZIP में बंधी CSV बेहतरीन है, और कई भाषाओं का सपोर्ट ठीक से न देने वाले पब्लिक ट्रांसपोर्ट के real-time train schedule वाले protobuf से मुझे यह कहीं ज़्यादा पसंद है
लेकिन अगर इसे एकल वैल्यू पाने वाली API की तरह ट्रीट करें, तो यह भारी बर्बादी है, और उम्मीद है कोई इसे अपने ऐप में इस तरह नहीं डालेगा
लेख अपने-आप में अच्छा है, लेकिन शीर्षक कुछ ज़्यादा ही उकसाने वाले दावे जैसा लगता है
इसे दिन में एक बार से ज़्यादा request करने की कोई वजह नहीं है, और ऐसे डेटा का इस्तेमाल करने वाले लोग संभवतः बहुत अलग-अलग filters या aggregations चाहेंगे
अगर मकसद मौजूदा exchange rate पाना है, तो यह सच में खराब design है, लेकिन उस काम के लिए दूसरी services हैं और यह फ़ाइल अपने typical use case के लिए अच्छी तरह fit बैठती है
API से सीधे जुड़ा नहीं है, लेकिन पहले जब मैं एक land management application support करता था, तो नई version आने से पहले वह धीमे satellite offices में भी ठीक चलती थी, जहाँ connection ISDN-स्तर का हो सकता था; नई version बिल्कुल नहीं चली
vendor ने कहा कि इसे RDP server पर चलाएँ, लेकिन मुझे यह बेतुका लगा, इसलिए जाँच की तो पता चला कि कोई एक call बिना किसी वजह
SELECT * FROM sometableकर रहा था, जबकि उसी execution के दूसरे calls ठीक-ठाक SQL select clause इस्तेमाल कर रहे थेजब हमने vendor को यह बताया, तो पहले वे बहुत उलझ गए कि हमें यह कैसे पता चला, और आखिरकार उन्होंने एक नई version निकाली जिसे धीमे connection पर भी इस्तेमाल किया जा सकता था
समझना मुश्किल है कि अपने testing में उन्होंने इसे क्यों नहीं पकड़ा और customer पर महँगा समाधान क्यों थोप दिया
आजकल अगर आपने JavaScript ज़रा भी देखा है, तो 565KB और उसमें से बड़ी value खोजने वाली logic किसी भी reasonable standard से बहुत छोटी है
कुछ लोग “बिना filtering के पूरा data मिल भी जाए, तो data पाने का तरीका” को API मानते हैं, लेकिन व्यक्तिगत रूप से मैं पूरी table download को data model download मानता हूँ, जहाँ model पर logic काम नहीं करती; API वह logic है जो model के उस हिस्से को filter करके लौटाती है जिसमें मेरी रुचि है
मैंने backend और frontend दोनों तरफ काफी financial software बनाया है, और frontend में असली data तक पहुँचने से पहले ही इतनी मात्रा का “data” transfer होना दुखद रूप से आम है
backend में यह बस design decision है, और इससे तेज़ कुछ नहीं कि nightly cron job exchange rates parse करे, मकसद के हिसाब से
todays-rates.jsonबनाए, और उसे static file के रूप में mobile, web और microservice apps को serve करेकहीं भी यह नहीं कहा गया कि mobile app को यह ZIP-CSV-over-HTTP सीधे consume करना ही होगा
जो लोग शिकायत करते हैं कि छोटी-सी data value चाहिए होने पर हर बार बड़ी फ़ाइल लेनी पड़ती है, उनके लिए एक बहुत सरल optimization है
अगर यह guarantee हो कि फ़ाइल append-only है और ZIP file के बजाय HTTP gzip/brotli जैसे compression का इस्तेमाल हो, तो range requests से आख़िरी update के बाद का सिर्फ नया data लिया जा सकता है
इसमें भरोसे के लिए एक checksum header और जोड़ दें, तो यह काफी efficient और बहुत simple incremental API बन जाती है
बेशक state रखना पड़ेगा, पहले download और state बनाए रखने की लागत चुकानी होगी, और अगर आपको 2007-08-22 का EUR/JPY exchange rate सिर्फ एक बार चाहिए, तो यह inefficient है
अभी बहुत work in progress है, लेकिन मौजूदा “research quality” code यहाँ है: https://pypi.org/project/csvbase-client/
https://github.com/gtsystem/python-remotezip
सिर्फ एक दिन का patch भी मेरी तरफ file को up to date रखने के लिए ज़रूरी bandwidth काफी घटा सकता है
यह तब की बात है जब रोज़ कुछ सौ KB extra download करना मायने रखता हो, और ज़्यादातर मामलों में शायद ऐसा नहीं होगा
sqliteexample में typo हैscreenshot में नहीं है, लेकिन
sqliteमें -csv argument जोड़ना होगामैं इसे फिर जोड़ूँगा और cache invalidate करूँगा। बच्चों को सुलाने के बाद देखूँगा कि क्या गड़बड़ थी
सुधार: मेरे environment में चलने की वजह यह थी कि
~/.sqlitercमें.separator ','setting थीलगता है पहले कभी यह समझकर कि मैं मुख्यतः CSV files डालता हूँ, इसे default के रूप में set कर दिया था
थोड़ा विषय से हटकर कहें तो, भले ही euro शुरुआत में सिर्फ electronically मौजूद था, eurozone member countries की मौजूदा currencies के साथ उसके fixed exchange rates थे
खासकर Germany की स्थापित और भरोसेमंद Deutsche Mark के साथ वह fixed था
इसलिए “शुरुआती euro कमजोर क्यों था” समझाने के लिए यह भी समझाना होगा कि उस समय DEM कमजोर क्यों था, और उस paragraph की explanation इस जाँच में पास होती नहीं लगती
छोटे problems में, जहाँ हर बार पूरा database download करके read-only तरीके से process किया जा सकता है, simplicity की value को कम नहीं आँकना चाहिए
मुझे SQLite पसंद है क्योंकि यह
.jsonया.csvfile की तरह portable है, लेकिन database की तरह interact करने के लिए ज़्यादा तैयार हैclickhouse-localइस्तेमाल करें तो पुरानी CSV file को भी database की तरह handle किया जा सकता हैअसली बात यहाँ है
इस case में जो चीज़ें नहीं करनी पड़ीं: access rights की negotiation, जैसे पैसे देना या sales person से बात करना, email address·company name·job title को किसी के leads database में डालना, quota का पालन करना, authentication करना, API documentation पढ़ना, basic format और structure से ज़्यादा गंभीर समस्याओं से निपटना
bandwidth free नहीं होती
SQLite ZIP files पढ़ और लिख सकता है
https://sqlite.org/zipfile.html
सोच रहा हूँ कि
gunzipके बजायsqlite3से decompression किया जा सकता है या नहींअगर file को disk पर store करना acceptable हो, तो ऐसा कर सकते हैं:
sqlite3 -newline '' ':memory:' "SELECT data FROM zipfile('eurofxref-hist.zip')" \| sqlite3 -csv ':memory:' '.import /dev/stdin stdin' \"select ...;"