- ECB’nin eurofxref-hist.zip dosyası yalnızca basit bir döviz kuru CSV paketidir, ancak sadece
curl, gunzip ve sqlite3 ile doların euro karşısında en güçlü olduğu tarih olan 2000-10-26 hemen bulunabilir
- Kaynak veri,
Date sütunundan sonra para birimi sütunlarının geldiği wide format yapısındadır; bu da analiz için elverişsizdir ve Date,Currency,Rate biçimindeki long format yapısına dönüştürülmesini gerektirir
- Her satırın sonundaki trailing comma nedeniyle CSV ayrıştırıcısı boş bir sütun daha okur; Pandas’ta
.iloc[:,:-1] ile son sütun kaldırılınca melt sonucu temiz hale gelir
- Düzenlenmiş CSV, HTTP PUT ile csvbase’e yüklenip
gnuplot, DuckDB ve sqlite3 gibi araçlarla birleştirilerek grafik çizme, hareketli ortalama hesaplama ve HTTP üzerinden CSV yükleme için kullanılabilir
- Erişim pazarlığı, kimlik doğrulama, kota ya da karmaşık API belgeleri olmadan alınabilen açık veri, open API gibi çalışır; basit bir zip dosyası bile finans uygulamaları için veri alışverişinin temeli olabilir
Tek bir zip dosyasıyla döviz kuru sorgulamak
- ECB, euro ile diğer para birimleri arasındaki geçmiş döviz kuru verilerini resmî bir zip dosyası olarak yayımlar
- Aşağıdaki işlem hattı veriyi indirir, arşivi açar, CSV’yi bellek içi bir SQLite veritabanına yükler, USD değerine göre sıralar ve ilk tarihi bulur
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;"
- Çıktı
2000-10-26 olur
curl -s, standart hata çıktısındaki gürültüyü azaltır; gunzip ise zip dosyasını açar
- Mac OS ya da BSD’de BSD tabanlı
gunzip zip dosyalarını desteklemediği için bunun yerine bsdtar -xOf - kullanılmalıdır
sqlite3 ':memory:' bellek içi bir veritabanı kullanır ve .import /dev/stdin stdin standart girdiyi stdin tablosuna aktarır
CSV biçimini düzenlemek ve Pandas melt
- Kaynak CSV başlığı
Date,USD,JPY,BGN,CYP,CZK,DKK,... şeklindedir; tarih sütunundan sonra para birimi sütunları gelir ve bu bir wide format örneğidir
- Filtreleme ve toplulaştırma yapmak için
Date,Currency,Rate biçimindeki long format daha kolay kullanılır
- Wide format’tan long format’a dönüştürme işlemine yaygın olarak melt denir
- Çoğu SQL veritabanında melt’e karşılık gelen bir işlem bulunmadığından, veri düzenleme için Pandas kullanışlıdır
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 dosyasında her satırın sonunda trailing comma bulunduğu için CSV ayrıştırıcısı sonda ek bir boş sütun okur
- Bu boş sütun
melt sonucunun sonunda gereksiz satırlar oluşturduğu için kaldırılması gerekir
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], tüm satırları ve son sütun hariç tüm sütunları seçer
- ECB döviz verisinin biçim olarak biraz düzenlenmesi gerekir, ancak buna rağmen erişim pazarlığı, ödeme, satış ekibiyle görüşme, e-posta/şirket adı/unvan gönderme, kota, kimlik doğrulama ya da API belgelerini okuma olmadan hemen kullanılabilir
- Yalnızca temel biçim ve yapı sorunları ele alındığında, açık veri yayınları arasında görece iyi durumdadır
Düzenlenmiş veriyi csvbase’e yüklemek
- Düzenlenmiş CSV, tekrar tekrar dönüştürme yapmamak için csvbase table üzerine yüklenebilir
- Mevcut işlem hattının sonuna bir
curl daha eklemek, CSV’nin HTTP PUT ile yüklenmesini sağlar
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 -, standart girdiden gelen veriyi belirtilen URL’ye yükler
- csvbase’te tablo yoksa yeni bir tablo oluşturur; varsa veriyi o tabloya yazar
-n, ~/.netrc içindeki kimlik bilgilerini kullanır
gnuplot ile döviz kuru grafiği çizmek
- Düzenlenmiş csvbase tablosundan
curl ile CSV alınabilir ve grep, cut, gnuplot ile birleştirilebilir
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'"
- Bu komut, 6.000’den fazla veri noktasını 80x25 karakter terminalinde ASCII art olarak belli ölçüde okunabilir biçimde çizer
gnuplot ayarları, CSV girdisini alıp tarih ile döviz kurunu çizgi grafik olarak çizmek üzere yapılandırılmıştır
set datafile separator ',': girdinin CSV olduğunu belirtir
set term dumb: ASCII art olarak çizer
plot -: veriyi standart girdiden alır
using 1:2 with lines: 1. ve 2. sütunları, yani tarih ve kuru, çizgi olarak çizer
title 'usd': çizginin adını usd olarak ayarlar
- Çıktı SVG olarak da üretilebilir; grafiğin zaman serisi gibi görünmesi için x ekseninin zaman olduğu belirtilmeli, zaman biçimi ve x ekseni etiketlerinin dönüşü ayarlanmalıdır
- Tekrar kullanım için bu işlem
plot_timeseries_to_svg adlı bir Bash fonksiyonunda toplanabilir
DuckDB ile hareketli ortalama hesaplamak
- USD döviz kurunun eğilimini görmek için DuckDB ile hareketli ortalama hesaplanabilir
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 yoksa aynı sorguyu sqlite3 için uyarlamak da zor değildir
- DuckDB, SQLite’a benzer ama satır odaklı değil sütun odaklıdır
- DuckDB, HTTP üzerinden CSV’yi doğrudan okuyup tablo dosyası oluşturabilir
CREATE TABLE eurofxref_hist AS SELECT * FROM
read_csv_auto("https://csvbase.com/calpaterson/eurofxref-hist");
- DuckDB tür çıkarımını oldukça iyi yapar, terminal boyutunu algılar ve büyük sonuçları varsayılan olarak sayfalı gösterir
- Büyük sorgularda ilerleme çubuğu gösterebilir ve Markdown tablo çıktısı da üretebilir
Açık verinin open API gibi çalışması
- Zip içindeki CSV ve
brew install veya apt install ile kolayca kurulabilen araçlar sayesinde pek çok iş yapılabilir
eurofxref-hist.zip, kurumlar arası veri alışverişi protokolü olarak son derece basit bir biçimdir
- Bu zip dosyası küçük görünebilir ama birçok finans uygulaması tarafından her gün kullanılır
- ECB’nin trailing comma’yı olduğu gibi bırakmasının nedeni, şimdi kaldırılması halinde çok sayıda kodun bozulabilecek olması olabilir
- Açık veri çok kolay erişilebilir sunulduğunda open API işlevi de görür
- Birçok API, uzaktan işlev çağrısından çok veri alışverişine benziyorsa, kolay indirilebilen açık veriden işlevsel olarak çok da farklı değildir
csvbase’in basit URL yapısı ve HTTP fiilleri
- csvbase, her tablo için tek bir URL kullanır
https://csvbase.com/<username>/<table_name>
https://csvbase.com/calpaterson/eurofxref-hist
- Her URL için dört temel HTTP fiili vardır
GET: CSV’yi alır; tarayıcıda web sayfası da alınabilir
PUT: yeni bir CSV ile yeni tablo oluşturur ya da mevcut tabloyu üzerine yazar
POST: mevcut tabloya CSV satırlarını toplu olarak ekler
DELETE: ilgili tabloyu siler
- Kimlik doğrulama için HTTP Basic Auth kullanılır
Veri düzenleme ve işlem hatları üzerine notlar
- SQL veritabanları içinde melt benzeri özellik sunan örnekler arasında Snowflake’in UNPIVOT özelliği ile MS SQL Server’ın PIVOT/UNPIVOT yapısı bulunur
- R ve Pandas’ın kullanılmasının önemli nedenlerinden biri, veri düzenleme yeteneklerinin güçlü olmasıdır
- Bash işlem hatları çoklu süreç olarak çalışır; her program bağımsız bir süreçte paralel yürür
- 2000 Ekim’inde doların euro karşısındaki kuru
0.8252 idi; bu, 1 dolar ile 1,21 euro alınabildiği anlamına gelir
- Euro, 1999 Ocak’ta banknot ve madeni para olmadan kullanıma girdi; başlangıçta yalnızca bankaların içinde vardı, banknotlar ve madeni paralar daha sonra geldi
1 yorum
Hacker News yorumları
ECB'de yaklaşık 15 yıl önce çalışırken bu dosyayı hatırlıyorum
Bu dosya ECB web sitesinde açık ara en çok indirilen dosyaydı; pek çok kişi ve finans kurumu her gün indirip kendi sistemlerini güncellemek için kullanırdı
Her gün belirlenen yayın saatinden hemen sonraki birkaç dakika içinde trafik ciddi şekilde sıçrardı; sıkıştırması açıldığında basit bir CSV dosyası olacak şekilde tasarlanması bilinçli bir karardı
Bu sayede dosya istikrarlı ve hızlı biçimde, az kaynakla sunulabiliyordu; o dönemde ECB'nin herkese açık web sitesinden sorumlu küçük ekip de bu veriyi tek bir statik dosya olarak sunma yönündeki teknik kararından haklı olarak büyük gurur duyabilirdi
Gösterişli değildir, framework de yoktur
Yaklaşık 15 yıl önce, herkesin muhtemelen ürünlerinden birini satın almış olduğu eski bir büyük şirkette; ürün kayıt sistemi ile birleşme ve satın almalardan kalan alt/paralel sistemler arasındaki veri alışverişiyle uğraştım. Bunların çoğu, sabit genişlikli dosyaların ya da ayraçlı dosyaların SFTP sunucuları üzerinden gönderilip alındığı toplu içe/dışa aktarma işleriydi
O sırada ürün zaten 15 yıllıktı; bu tür veri kaynakları ya da dışa aktarımlar 20-30 kadar gidip geliyordu ama gayet iyi çalışıyordu
Bugün de büyük değişiklik olmadan kullanılıyor olma ihtimali yüksek; o dönemde frontend ise Smalltalk ile yazılmış eski sürümden yeniden yazılıyordu
Kullandığımız veri kaynakları arasında uğraşması en kolay olanıydı
Mimar, ZIP'in bu amaca yönelik şartnameye uygun bir format olmadığını söyler; uyum ekibi kişisel veri sızıntısı kontrolü gerektiğini söyler; risk tarafı da kötü niyetli aktörlerin dosyayı indirmesinin engellenmesi gerektiğini söylerdi
Web ekibi ise siteye bir şey eklemek için onaylı değişiklik süreci gerektiğini söylerdi muhtemelen
Basit bir dosya indirme ve CSV dosyası harikadır
Keşke daha fazla yer veriyi böyle basit formatlarda yayımlasa; ABD kamu verisi indirmelerinde her “alışveriş sepeti” doldurmam gerektiğinde içimden bir parça ölüyor gibi hissediyorum
Bu belirli pipeline'ı kolaylaştıran pek çok wrapper aracı da var; web görünümü ve biraz daha gelişmiş özellikler gerekirse Datasette gibi şeyler de iyi
ZIP dosyasını stream olarak okuyup CSV'yi satır satır işleyerek dönüştürebilir, ardından Postgres için COPY FROM stdin kullanarak veritabanına yükleyebilirsiniz
O kadar mantıklı ve kullanışlı görünüyor ki, şimdiye kadar karşıma çıkmamış olmasına şaşırdım
CSV biçiminde çok raporum var; hızlı sorgular çalıştırmak için bir an önce denemek istiyorum
Örneğin
"Look, this contains \"quotes\"!",012345ile"Look, this contains ""quotes""!",012345gibi tırnak işleme biçimleri ayrışır; daha bozuk örnekler olarak"Look, this contains "quotes"!",012345veyaLook, this contains "quotes"!,012345de çıkabilirElektronik tablonun izleri olarak
"Look, this contains ""quotes""!",12345örneğindeki gibi baştaki 0 da kesilebilirTeoride JSON da elle düzeltilip yarı bozuk bir dosyaya dönüşebilir; ama pratikte JSON dosyalarına böyle yapıldığını neredeyse hiç görmedim. Seri numarası gibi değerler de JSON'da, “yardımsever” bir uygulamanın baştaki 0'ı kırpacağı bir tamsayı olmak yerine genellikle string olarak kalır
Neden böyle yapılıyor ki; meşru bir gerekçesi var mı?
CSV'yi ZIP'lenmiş bir JSON belgesiyle değiştirseniz de avantajlar aynı olur
Asıl sorun, statik olarak sunulan tek bir dosyayı basitçe indirmek için önünüze çok fazla engel konması
Bir kamu kurumu için API yapmıştım; veri yılda bir kez değişiyor ya da çok nadiren revize ediliyordu
Tüm veri seti 1 MB'tan küçük tek bir ZIP dosyasına sığabilirdi, ama çözüm mimarı gereksinimleri belirlerken iş büyüdü
Talep edildiği anda verinin değişmiş olabileceği gerekçesiyle cache kullanımını engelledi; sonuçta yavaş bir API ortaya çıktı ve veri değişikliklerini abonelere bildiren gereğinden karmaşık bir webhook sistemi bile oluştu
Tek bir ZIP dosyası fazla basit olabilirdi, ama gerçekte ihtiyaç duyulandan da çok farklı değildi
Daha havalı yapmak isterseniz, dosya değiştiğinde tetiklenen bir webhook ekleyerek istemcinin günde bir kez polling yapmak yerine ne zaman tekrar indireceğini bilmesini sağlayabilirsiniz
Ya da değişiklik olduğunda önceden belirlenmiş bir e-postayı bir mailing list'e gönderen basit bir script bile yeterli olur
Öncekinden farklı bir şey yoksa boş bir HTTP 304 yanıtı alırsınız; değişmişse yeni ETag ile birlikte 1 MB'tan küçük ZIP dosyasını tekrar alırsınız. Burada neyin eksik olduğunu bilmiyorum
Cache karmaşıklığı artırır ve cache'i elle yeniden doğrulama riski de doğurur; bu yüzden çözüm mimarı haklı olmuş olabilir
Tek bir sonuç değeri olan
2000-10-26için 565 KB’lık bir dosya indirmek zorundaysanız, bu korkunç bir API’dir.Çok miktarda veri alıp kullanıcıya yeniden sunmak istiyorsanız ZIP’e konmuş CSV harikadır; çoklu dil desteği iyi olmayan toplu taşıma gerçek zamanlı tren saatleri için protobuf’tan çok daha fazla tercih ederim.
Ama bunu tek bir değer döndüren bir API gibi ele alırsanız muazzam bir israf olur; umarım kimse bunu bir uygulamaya bu şekilde koymaz.
Yazının kendisi güzel, ama başlık fazla kışkırtıcı bir iddia gibi hissettiriyor.
Günde birden fazla kez istemek için hiçbir neden yok; bu veriyi kullananların birbirinden çok farklı filtreler veya toplulaştırmalar istemesi muhtemel.
Güncel döviz kurunu almak içinse kötü bir tasarım olduğu doğru, ama o amaç için başka hizmetler var ve bu dosya tipik kullanım senaryosuna gayet uygun.
API ile doğrudan ilgili değil ama eskiden bir arazi yönetimi uygulamasına destek verirken, yeni sürüm çıkana kadar ISDN düzeyinde hat olabilecek yavaş uydu ofislerinde bile iyi çalışıyordu; yeni sürüm ise hiç çalışmadı.
Tedarikçi bunu RDP sunucusunda çalıştırmamızı söyledi, ama bunun saçma olduğunu düşünüp araştırınca, çağrılardan birinin hiçbir neden yokken
SELECT * FROM sometableyaptığını, aynı çalıştırmadaki diğer çağrıların ise düzgün SQL select cümlecikleri kullandığını gördük.Bunu tedarikçiye söylediğimizde başta bunu nasıl öğrendiğimiz konusunda epey kafaları karıştı; sonunda yavaş hatlarda da kullanılabilecek şekilde düzeltilmiş yeni bir sürüm çıkardılar.
Kendi testlerinde bunu neden yakalayamadıklarını ve müşteriye pahalı bir çözümü neden dayattıklarını anlamak zor.
Bugünlerde JavaScript’e azıcık bile baktıysanız, 565 KB ve bunun içinde büyük değeri bulma mantığı makul herhangi bir ölçüte göre çok küçüktür.
Bazıları “filtreleme olmadan tüm veriyi alsanız bile veriye erişmenin yolu”nu API sayıyor; ben şahsen tüm tabloyu indirmeyi, üzerinde mantığın çalışmadığı bir veri modeli indirmek olarak görüyorum. API ise modelin ilgilendiğim şekilde bir bölümünü filtreleyip döndüren mantıktır.
Hem backend hem frontend tarafında çok finans yazılımı geliştirdim; frontend’de gerçek veriye ulaşmadan önce bile bu miktarda “veri” aktarmak ne yazık ki yaygın.
Backend’de bu sadece bir tasarım kararıdır; her gece çalışan bir cron işinin döviz kurlarını ayrıştırıp amaca uygun bir
todays-rates.jsonüretmesi ve bunu mobil, web ve mikroservis uygulamalarına statik dosya olarak sunmasından daha hızlısı yoktur.Mobil uygulamanın bu ZIP-CSV-over-HTTP’yi mutlaka doğrudan tüketmesi gerektiğini söyleyen hiçbir şey yok.
Küçük bir veri parçasına her ihtiyaç duyduğunda büyük dosya indirmek zorunda kalmaktan şikâyet edenler için çok basit bir optimizasyon var.
Dosyanın yalnızca eklemeli olduğu garanti edilirse ve ZIP dosyası yerine HTTP gzip/brotli gibi bir sıkıştırma kullanılırsa, range request ile son güncellemeden bu yana gelen yeni veriler alınabilir.
Buna güvence için bir checksum header’ı ekleyince oldukça verimli ama yine de çok basit bir artımlı API olur.
Elbette durum saklamanız gerekir, ilk indirme ve durumu koruma maliyetini ödersiniz; 2007-08-22 tarihindeki EUR/JPY kurunun yalnızca tek bir değerine bir kez ihtiyaç duyduğunuzda ise verimsizdir.
Hâlâ yoğun biçimde çalışma aşamasında, ama mevcut “araştırma kalitesindeki” kod burada: https://pypi.org/project/csvbase-client/
https://github.com/gtsystem/python-remotezip
Tek günlük bir patch bile dosyayı kendi tarafımda güncel tutmak için gereken bant genişliğini ciddi biçimde azaltabilir.
Bu, günde birkaç yüz KB daha az indirmenin anlamlı olduğu durumlar için geçerli; çoğu zaman muhtemelen öyle değildir.
sqliteörneğinde bir yazım hatası var.Ekran görüntüsünde yok, ama
sqlite’a -csv argümanını eklemek gerekiyor.Yeniden ekleyip cache’i geçersiz kılacağım. Çocukları yatırdıktan sonra neyin yanlış gittiğine bakmayı planlıyorum.
Düzeltme: Benim ortamımda çalışmasının nedeni
~/.sqliterciçinde.separator ','ayarının olmasıymış.Geçmişte çoğunlukla CSV dosyaları içe aktardığımı fark edip bunu varsayılan olarak ayarlamış olmalıyım.
Kısa bir parantez açarsak, euro başlangıçta yalnızca elektronik olarak var olmuş olsa bile euro bölgesi üyesi ülkelerin mevcut para birimleriyle sabit döviz kurları vardı.
Özellikle Almanya’nın yerleşik ve güvenilir Deutsche Mark’ına sabitlenmişti.
Dolayısıyla “ilk dönemde euro neden zayıftı”yı açıklamak için o dönemde DEM’in neden zayıf olduğunu da açıklamak gerekir; ilgili paragraftaki açıklama bu kontrolden geçemiyor gibi.
Her seferinde tüm veritabanını indirip salt okunur şekilde işleyebileceğiniz küçük problemlerde sadeliğin değerini hafife almamak gerekir.
SQLite’ı seviyorum;
.jsonveya.csvdosyaları kadar taşınabilirken veritabanı gibi etkileşime çok daha hazır.clickhouse-localkullanırsanız eski CSV dosyalarını da veritabanı gibi ele alabilirsiniz.Asıl mesele burada.
Bu durumda yapılması gerekmeyenler: erişim izni pazarlığı, örneğin para ödemek veya bir satış temsilcisiyle konuşmak; e-posta adresinizi, şirket adınızı ve unvanınızı birinin potansiyel müşteri veritabanına sokmak; kota gözetmek; kimlik doğrulamak; API dokümanı okumak; temel format ve yapıdan daha ciddi sorunlarla uğraşmak.
Bant genişliği bedava değil.
SQLite ZIP dosyalarını okuyup yazabiliyor.
https://sqlite.org/zipfile.html
gunzipyerinesqlite3ile sıkıştırma açmak mümkün mü merak ediyorum.Dosyayı diske kaydetmek sorun değilse şöyle yapabilirsiniz:
sqlite3 -newline '' ':memory:' "SELECT data FROM zipfile('eurofxref-hist.zip')" \| sqlite3 -csv ':memory:' '.import /dev/stdin stdin' \"select ...;"