Reading the ECB fx rates file with Python (pandas)

A while ago (https://www.uweziegenhagen.de/?p=2373) I had an article on how to read the ECB fx rates file with Python. Some time has passed, there are other options in Python 3.

Option 1: Make the Python 2 code run with Python 3

import xml.etree.ElementTree as ET
import urllib.request 
 
root = ET.parse(urllib.request.urlopen('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')).getroot()
 
for child in root[2][0]:
    curr = child.get('currency')
    rate = child.get('rate')
    print(curr, rate)

Option 2: Use pandas >=1.3

Starting with version 1.3 pandas offers the read_xml command, so upgrade using
pip3 install --upgrade pandas or conda update pandas.

from urllib.request import urlopen
import pandas as pd
 
df = pd.read_xml(urlopen('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'),xpath='//*[@currency]')
 
print(df)

Pocketmod – TODO Listen in A3

Zum Thema Pocketmod hatte ich schon früher etwas geschrieben (https://www.uweziegenhagen.de/?p=1294), hier nun ein Template für ein TODO-Listen Pocket in DIN A3.

Das fertige PDF sieht dann so aus: PocketMod_for_A3

\documentclass[21pt]{scrartcl}
\usepackage[left=2cm,right=2cm, top=2cm,bottom=2cm,a3paper]{geometry}
 
\usepackage{forloop}
\setlength{\parindent}{0pt}
\usepackage{tikz}
 
\newcounter{ct} 
\usepackage{pgfpages}
 
\def\pgfpageoptionheight{\the\paperwidth} % landscaped by default
\def\pgfpageoptionwidth{\the\paperheight}
\def\pgfpageoptionborder{0pt}
\def\pgfpageoptionfirstshipout{1}
 
\pgfpagesphysicalpageoptions{%
    logical pages=8,%
    physical height=\pgfpageoptionheight,%
    physical width=\pgfpageoptionwidth,%
    current logical shipout=\pgfpageoptionfirstshipout%
}
 
    \pgfpageslogicalpageoptions{1}{%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.875\pgfphysicalwidth}{.75\pgfphysicalheight}%
    }%
    \pgfpageslogicalpageoptions{2}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      rotation=180,%      
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.875\pgfphysicalwidth}{.25\pgfphysicalheight}%
    }%
 
    \pgfpageslogicalpageoptions{3}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      rotation=180,%      
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.625\pgfphysicalwidth}{.25\pgfphysicalheight}%
    }%
    \pgfpageslogicalpageoptions{4}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      rotation=180,%      
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.375\pgfphysicalwidth}{.25\pgfphysicalheight}%
    }%
 
    \pgfpageslogicalpageoptions{5}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      rotation=180,%
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.125\pgfphysicalwidth}{.25\pgfphysicalheight}%
    }%
    \pgfpageslogicalpageoptions{6}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.125\pgfphysicalwidth}{.75\pgfphysicalheight}%
    }%
 
    \pgfpageslogicalpageoptions{7}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.375\pgfphysicalwidth}{.75\pgfphysicalheight}%
    }%
    \pgfpageslogicalpageoptions{8}
    {%
      border shrink=\pgfpageoptionborder,%
      resized width=.25\pgfphysicalwidth,%
      border code=\pgfusepath{stroke},%
      resized height=0.5\pgfphysicalheight,%
      center=\pgfpoint{.625\pgfphysicalwidth}{.75\pgfphysicalheight}%
    }%
 
 
\begin{document}%
 
\forloop{ct}{1}{\value{ct} < 9}{%
\begin{tikzpicture}
\foreach \i in {0,...,-17}{%
\draw[very thick] (0,\i*2) -- ++(0,1.5) -- ++(1.5,0) -- ++(0,-1.5)--cycle; 
\draw[very thick] (2.5,\i*2) -- (22,\i*2);
\draw[very thick] (23,\i*2) -- ++(0,1.5) -- ++(1.5,0) -- ++(0,-1.5)--cycle; 
}
\end{tikzpicture}
\clearpage
}
 
\end{document}

CSV-Dateien effizient vergleichen mit pandas

Hier ein bisschen Python-Code, um zwei CSV Dateien miteinander zu vergleichen. Die Ergebnisse des spalten- und zeilenweisen Vergleichs werden dann zusammengefasst dargestellt, um schnell einen Überblick zu bekommen, wo eine tiefergehende Analyse notwendig ist.

import sys
import collections
import pandas as pd
from tabulate import tabulate
 
 
file1 = pd.read_csv('file1.csv', sep=';', encoding='UTF-8')
file2 = pd.read_csv('file2.csv', sep=';', encoding='UTF-8')
 
columnnames1 = list(file1)
columnnames2 = list(file2)
 
if collections.Counter(columnnames1) == collections.Counter(columnnames2):
    print ("Number of columns and Names match, Comparison possible...\n\n")
else:
    print ("Number of columns and Names are not matching!!! Please check the input!")
    sys.exit('Error!')
 
# add suffixes to distinguish between actual and expected in the merger
file1 = file1.add_suffix('_e') # expected
file2 = file2.add_suffix('_t') # t
 
 
# merge them using the given key, use outer join
comparison = pd.merge(file1,file2, how='outer',
                      left_on=['Key_e'],
                      right_on=['Key_t'])
 
# create the columnwise comparison
for col in columnnames1:
    comparison[(col + '_c')] = comparison[(col + '_t')] == comparison[(col + '_e')]
 
# reorder the columns
comparison=comparison.reindex(sorted(comparison.columns),axis=1)
 
print(tabulate(comparison, tablefmt="pipe", headers="keys"))
 
 
# save the result as Excel file
comparison.to_excel('result.xlsx')
 
# names of the comparison column
check_colnames= [s + '_c' for s in columnnames1]
 
# initialize an empty dataframe for the log
logdf=pd.DataFrame(index=[True,False])
 
for column in check_colnames:
    t=comparison[column].value_counts() # returns a series
    tt=pd.DataFrame(t) # makes a DF out of the series
    logdf = logdf.join(tt,how='outer') # join the two dfs
 
# transpose for better readability
logdf = logdf.transpose()
 
# Ensure fixed sequence of the columns
logdf=logdf.reindex(sorted(logdf.columns),axis=1)
 
# write to disk
logdf.to_excel('logfile.xlsx')
 
# for better viewing on the screen
logdf.fillna('-',inplace=True)
pd.options.display.float_format = '{:,.0f}'.format
 
print(tabulate(logdf, tablefmt="pipe", headers="keys"))

Powershell: Dateien finden, die die 256-Zeichen-Pfadlänge sprengen

Sharepoint-Server haben ein Maximum von 256 Zeichen für Pfadangaben, Leerzeichen in Datei- und Ordnernamen werden dabei als „%20“ dargestellt, sodass für jedes Leerzeichen drei Zeichen „draufgehen“. Mit dem folgenden Powershell-Skript kann man die Dateien in einem Verzeichnis identifizieren, die das Limit vermutlich sprengen.

Die erzeugte CSV-Datei hat drei Spalten: Pfadlänge, Pfadlänge wenn Leerzeichen als „%20“ dargestellt werden, Pfad.

gci "K:\inputpath" | Select @{N="Path Length";E={$_.FullName.Length}}, @{N="URL Length";E={$_.FullName.replace(' ','+++').Length}}, 
Fullname | Export-Csv -NoTypeInformation -Delimiter ";" -Path "t:\ABC\filelaengen.csv"

Excels Wenn ohne Wenn

Vor ein paar Wochen hat jemand in einer Facebook-Gruppe gefragt, wie man — abhängig von einer Soll/Haben-Spalte — in Excel einen Wert mit positivem oder negativem Vorzeichen darstellen kann. Die Lösung ist ganz einfach, wenn man die WENN() Funktion kennt.

Spannender ist die Frage: Geht es auch ohne Wenn() und ohne VBA? Die Antwort ist ja, von hinten durch die Brust ins Auge…

Mittels CODE() Funktion ermitteln wir den ASCII Code des Buchstabens, bei „S“ ist das 83, bei „H“ 72.

Als nächstes ziehen wir von diesem Wert 84 ab und vergleichen den Wert mit -1. Excel wertet dies für „S“ als WAHR aus, für „H“ als FALSCH. Da man mit WAHR (=1) und FALSCH (=0) prima in Excel weiterrechnen kann, multiplizieren wir den Wert mit -2 und addieren 1.

Für „S“ ergibt sich 1 (für „WAHR“) * -2 = -2 + 1 = 1, für „H“ ergibt sich 0 (für „FALSCH“) * -2 = 0 + 1 = 1. Damit muss man dann nur noch den ursprünglichen Betrag multiplizieren…

Gittermuster erstellen mit gridpapers

Hier ein Beispiel dafür, wie man mit dem gridpapers Paket Muster auf Papier bringen kann, hier ein Punktmuster.

%!TEX TS-program = Arara
% arara: pdflatex: {shell: yes}
\documentclass[12pt,ngerman]{scrartcl}
\areaset{8cm}{16cm}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{babel}
\pagestyle{empty}
\usepackage[pattern=dot,% 
colorset=std,
geometry={left=2.25cm,right=1.25cm,top=1cm,bottom=1.25cm},%
textarea,%
patternsize={5mm},%
dotsize={1pt}
]{gridpapers}
 
\begin{document}
~
 
\end{document}

Auszug aus der A4-Seite:

Dymo Labels mit LaTeX und ticket.sty gestalten

Hier ein kurzes Beispiel, wie man mit LaTeX auch Dymo-Labels erzeugen kann, im Beispiel für die Label-Größe 57*32mm. Zum allgemeinen Verständnis von ticket.sty siehe auch meinen DTK Artikel in Ausgabe 1/2021.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{graphicx}
\usepackage[landscape,paperheight=57mm,paperwidth=32mm,left=0mm,top=0mm,right=0mm,bottom=0mm]{geometry}
%\usepackage[sfdefault]{plex-sans}
\usepackage{palatino}
 
\begin{filecontents}[overwrite]{Dymo5732.tdf}
\unitlength=1mm
\hoffset=-25.4mm
\voffset=-29mm
\ticketNumbers{1}{1}
\ticketSize{57}{32} % Breite und Hoehe der Labels in mm
\ticketDistance{0}{0} % Abstand der Labels
\end{filecontents}
 
\usepackage[Dymo5732]{ticket}
 
\renewcommand{\ticketdefault}{}%
\makeatletter
\@boxedfalse % Rahmen um Ticket
\@emptycrossmarkfalse % Falzmarken
\@cutmarkfalse % Schnittmarken
\makeatother
 
\newcommand{\myticket}[2]{
\ticket{%
\put(10,10){\scalebox{#1}{\bfseries #2}}
}}
 
\newcommand{\myticketml}[4]{
\ticket{%
\put(5,20){\scalebox{#1}{\bfseries #2}}
\put(5,15){\scalebox{#1}{\bfseries #3}}
\put(5,10){\scalebox{#1}{\bfseries #4}}
}}
 
 
\begin{document}
\myticketml{1.25}{Dr. Max  Mustermann}{Musterweg 123}{54321~Musterstadt}
\myticket{2}{Steuern}
\end{document}

PDF

Nützliche Kommandozeilen-Einzeiler für LaTeX

Unter https://gist.github.com/iwishiwasaneagle/2f91f63f3cb0107b94b501aa284a18ca gibt es eine Sammlung an nützlichen Einzeilern für die Arbeit mit LaTeX.

Windows-Uhr: Sekundenanzeige aktivieren

Die Uhr rechts unten in der Windows-Taskleiste kann man auch so einstellen, dass die Sekunden angezeigt werden

Dazu mit regedit den Schlüssel \HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced –> ShowSecondInSystemClock auf 1 setzen.

Aktienkurse mit Python und LaTeX auswerten

Hier ein einfaches Beispiel, wie man mit Python und LaTeX ein PDF mit Kursinformationen erstellen kann.

Zuerst der Python-Teil, der die Apple-Kursdaten seit dem 1.1.2021 in einen Dataframe lädt und dann in eine LaTeX-Tabelle schreibt:

import pandas
import pandas_datareader.data as web
 
YAHOO_TODAY="http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sd1ohgl1vl1"
 
history = web.DataReader('AAPL', "yahoo", start="2021-1-1")
history.to_latex('aapl.tex')

Dann noch der LaTeX-Teil, der a) den Python-Code aus dem LaTeX-Lauf heraus ausführt und b) die erzeugte Tabellen-Datei nur dann einbindet, wenn sie wirklich auch erzeugt wurde.

\documentclass[12pt,ngerman]{scrartcl}
\usepackage[a4paper, top=1cm,bottom=1cm,left=1cm, right=1cm]{geometry}
\usepackage[T1]{fontenc}
\usepackage{booktabs}
 
\makeatletter
\newcommand{\testfileexists}[1]{%
  \IfFileExists{#1}%
    {\def\inputtestedfile{\@@input #1 }}
    {\let\inputtestedfile\@empty}%
}
\makeatother
 
\begin{document}
 
\write18{python runpy.py}
 
  \testfileexists{aapl}
      \inputtestedfile
 
 
\end{document}