Nie wiem, czy to już wzrok zawodzi, czy co, ale jak dla mnie okienko explorera w VSC jest jakoś takie nieczytelne. Foldery są mało widoczne, zlewają sie z plikami itp. Ale da sie to zmienić!
W ustawieniach (File >> Preferences >> Settings) VSC odszukaj ustawienia „tree indent”. Ja ustwiłem sobie tą wartość na „zabójcze” 32, ale 16 też znacznie poprawia widoczność wcięć:
Gdy w końcu dojrzejesz do decyzji o wymianie dysku na większy, nie chcesz przechodzić przez proces instalacji systemu operacyjnego, instalacji oprogramowania, konfiguracji itp. itd. W takim przypadku pomocne może być klonowanie dysku.
Istnieje dużo aplikacji, które pozwolą sklonować dysk, problem tylko w tym, jak sobie te programy poradzą zwłaszcza z utrzymaniem możliwości bootowania ze sklonowanego dysku. Dlatego ku pamięci opiszę tu z jakich narzędzi korzystałem, a jakie zawiodły i dlaczego.
Mój plan nr 1 to skorzystanie z oprogramowania dodawanego do dysku. Zdecydowałem się na dysk Western Digital Black Edition, a WD dodaje do niego licencję na Acronis True Image. Program ten ma zadziałać z całą mocą jeśli tylko wykryje dysk WD podpięty do systemu. Rozpakowałem i przy pomocy obudowy Natec Rino podłączyłem dysk do komputera. Ze strony WD pobrałem Acronis-a. Zainstalowałem, a potem otworzyłem program i… zobaczyłem komunikat o błędzie aktywacji licencji. Oprogramowanie nie widziało dysku WD i nie chciało się aktywować. Rzeczywiście dysk identyfikował się w systemie jako Jmicron i stało się tak raczej nie dlatego, że sprzedawca sprzedał podróbkę, ale po prostu WD przejęło firmę Jmicron. Wypełniłem więc zgłoszenie usterki i machnąłem ręką na WD i Acronisa. Szkoda, bo większość dysków, jakie mam to WD i jak do tej pory nie miałem z nimi większych problemów, a tu taka klapa. Gdyby jednak wszystko zadziałało, pewnie kontynuowałbym tak jak na tym video albo tak, jak opisali to w instrukcji.
Co ciekawe, już we wspomnianej instrukcji pisano, że nowy dysk musi być zainstalowany w komputerze, a stary może się znajdować „na zewnątrz”. Podobno połączenie dysków odwrotnie (stary w komputerze, nowy w obudowie na zewnątrz), może spowodować, że nowy dysk po klonowaniu nie będzie sie bootował. Tymczasem…. znalazłem lepsze rozwiązanie, które takiego problemu nie miało.
Clonezille można pobrać w formacie ISO, u mnie plik nazywał się clonezilla-live-3.2.0-5-amd64. Ten obraz ISO należy następnie przegrać np. na pendrive, ale w taki sposób, aby dysk USB był bootowalny i właśnie w tym przegraniu obrazu pomaga Rufus.
Tak więc:
pobrałem Clonezille
pobrałem Rufusa
znalazłem pen drive
włożyłem pen drive do portu USB
uruchomiłem Rufusa i przeniosłem obraz ISO clonezilli na pendrive
Następnie dokładnie tak, jak jest to opisane we wspomnianym artykule
podłączyłem nowy dysk do komputera za pomocą zewnętrznej obudowy
uruchomiłem komputer z pendrive
krok po kroku powiedzialem Clonezilli, co chcę zrobić. Trzeba tylko uważać na wybierane opcje. Przy drobnym błędzie można sobie sklonować nowy (pusty) dysk na stary (z twoimi cennymi danymi)
potem wyłączyłem komputer i wymieniłem dyski miejscami. Teraz nowy dysk był już w komputerze
następnie trzymając kciuki włączyłem komputer i WSZYSTKO DZIAŁALO
Może jedyna wada tego rozwiązania była taka, że na nowym dysku powstał układ systemu plików i podział partycji, taki sam ja na oryginalnym dysku. W moim przypadku migracja była z dysku 500GB na dysk 2TB, więc po prostu utworzyłem dodatkowy wolumen przeznaczając na niego to dodatkowe wolne miejsce. Może tak i jest lepiej?
Podsumowując: zawiodłem się na bonusach rozdawanych przez WD w postaci programu Acronis True Image. Nadal uważam, że dyski WD są dobrej jakości. Znowu przekonałem się, że open source i rozwiązania oparte o Linuxa prezentują świetną jakość. Polecam Clonezille i Rufusa!
GitHub jest super, ale kiedy chcesz zautomatyzować co nieco w zakresie pracy z kodem, to przyda się praca z linii komend. Jeśli jeszcze nie masz zainstalowanego GitHub CLI to pobierz je.
Zaczynamy w cmd od przejścia do właściwego repo. To ważne, bo chociaż komendy gh pozwalają przy pomocy parametru wskazać, na którym repo będą wykonywane pewne czynności, to wygodnie jest korzystać z domyślnego repo, którym jest to, gdzie uruchomiło się gh
Pracę zaczyna się od zalogowania.
gh auth login
Teraz zależnie od konfiguracji mogą wydarzyć się różne rzeczy, ale np. autoryzować można się za pomocą Personal Access Token (PAT). Wystarczy wkleić PAT i już zostaniemy uwierzytelnieni jako właściwy użytkownik z właściwym dostępem.
Aby wyświetlić oczekujące Pull Request uruchom
gh pr list
W odpowiedzi wyświetli się lista oczekujących pull requestów. Można podejrzeć metadane uruchamiając polecenie view z odpowiednim numerem pr:
gh pr view 2
Żeby zobaczyć jakie zmiany idą za pull request uruchom
gh pr diff 2
Jednym z zadań do wykonania podczas akceptacji pull request jest wykonanie review, a w tym review często dodajemy komentarz (opcja c odpowiada za komentarz, a opcja b za blok tekstu):
gh pr review 2 -c -b „good idea!”
Oprócz review można też zaakceptować request:
gh pr review 2 –approve
No i wreszcie pull request można dołączyć do brancha:
gh pr merge 2
Do kompletu zostałoby jeszcze tylko może w jaki sposób utworyć nowy pull request. Zrobisz to tak:
gh pr create –base main –head „automation”–title „Automation added” –body „Added automation to data processing”
private – wyizolowana sieć, bez dostępu do sieci hosta
internal – wewnętrzna wirtualna sieć, do której dostęp może mieć komputer wirtualny, jak i system hosta, co za tym idzie, można udostępnić Internet
external – maszyna wirtualna „widzi” tę samą sieć, co host
Jeśli chcesz, aby maszyna wirtualna miała dostęp do Internetu, to przez eliminację odpada sieć private. Można by skorzystać z sieci external, ale wtedy jest delikatny problem z adresem IP komputera wirtualnego, bo jest on przyznawany przez DHCP, a możesz preferować jednak stały adres IP. Ewentualne skonfigurowanie statycznego adresu IP na maszynie wirtualnej może doprowadzić do konfliktów, a tego byśmy nie chcieli. I tak zostaje sieć internal.
No to po kolei:
Otwórz managera Hyper-V i w Virtual Switch Manager utwórz sieć typu internal. Możemy ją nazwać np. InternalSwitch. Można to też zrobić poleceniem PowerShell:
Na komputerze hosta w PowerShell uruchomionym jako administrator uruchom kolejno polecenia, które skonfigurują tę maszynę do NAT:
Przypisanie do interfejsu hosta odpowiadającego za komunikację z siecią wewnętrzną stałego adresu IP (tutaj 192.168.0.1). Jeśli nie lubisz PowerShella, można to też wyklikać
Konfiguracja wewnętrznej sieci do NAT. Tutaj trzeba użyć PowerShella, lub ewentualnie dodać do hosta rolę RRAS (Routing and Remote Access Server), ale to dość dużo pracy w porównaniu do jednej komendy
Network manager to sprytna bestia. Można dla jednego interfejsu sieciowego zdefiniowac np. polaczenie net-static, ktore przypisuje temu interfejsowi adres statyczny oraz polaczenie net-dynamic, ktore przydziela interfejsowi adres z DHCP. W danej chwili może być aktywne tylko jedno połącznie, ale w ramach potrzeb można się przełączyć z jednej konfiguracji do drugiej. Zwykle jedno połączenie odpowiada jednemu interfejsowi, ale… warto wiedzieć, że się da 🙂
Apache Spark (spark) pracuje na obiektach data frame podobnych do tych z Pandas, ale zoptymalizowanych do pracy z Spark engine. Tutaj wczytujemy (read) plik csv (format), który w pierwszym wierszu ma nagłówek (option) z pliku (load):
df = spark.read.format("csv").option("header","true").load("Files/orders/2019.csv")
# df now is a Spark DataFrame containing CSV data from "Files/orders/2019.csv".
display(df)
Plik CSV może nie mieć nagłówka i wtedy po jego wczytaniu kolumny będą miały nazwy _c01, _c02, … Dlatego można „nałożyć” na plik CSV strukturę. W takim przypadku będziemy pracować z czymś, co przypomina tabelę SQL. Rzeczywiście w spark mamy definicje typów SQL. Tutaj wczytujemy bibliotekę pyspark.sql.types, a następnie tworzymy strukturę (StructType), która składa się z kolumn (StructField), a te kolumny mogą być różnego typu, np.: StringType, IntegerType, DateType, FloatType. Zmienia się też sposób wczytania danych. Nadal czytamy (read) dane w postaci csv (format), ale teraz nadajemy im strukturę (schema), a same dane pobieramy z pliku (load).
W oparciu o jeden data frame można tworzyć kolejne. Można np. wybrać tylko niektóre kolumny wymieniając je w nawiasach kwadratowych. Data frame ma automatycznie kilka przydatnych funkcji. Np. count() policzy ile wierszy ma data frame, distinct() zwróci tylko wiersze unikalne, a co za tym idzie distinct().count() policzy ile wierszy jest unikalnych:
Zamiast wymieniania nazw kolumn w nawiasie kwadratowym, można też użyć jawnej funkcji select:
customers = df.select("CustomerName", "Email")
Data frame ma metodę where, która pozwala odfiltrować wiersze spełniające określone warunki. Warunek definiuje się odwołując się do kolumn danych. Np. tutaj filtr wskazuje, że należy wyświetlić te wiersze, które w kolumnie Item mają określony tekst:
Dane z data frame można grupować. Wystarczy wskazać, które kolumny zawierają dane „po których” ma się odbywać grupowanie, oraz wybrać funkcję agregującą:
Spark może się posługiwać funkcjami występującymi w SQL. Te funkcje znajdują się w module pyspark.sql.functions. Dzięki temu można uruchomić funkcję, np. tutaj year, przekazać do niej kolumnę (col), oraz nadać jej alias (alias). Dodatkowo dane można posortować (orderBy):
from pyspark.sql.functions import *yearlySales = df.select(year(col("OrderDate")).alias("Year")).groupBy("Year").count().orderBy("Year")display(yearlySales)
Podczas transformacji można wyznaczać nowe kolumny, których wartość jest zbudowana w oparciu o istniejące kolumny. W tym celu korzysta się z withColumn, której argumentem jest nazwa nowo tworzonej kolumny oraz wyrażenie wyznaczające wartość tej kolumny. W wyrażeniach można używać funkcji takich, jak: year, month, split, getItem. Kolumny można eliminować lub zmieniać ich kolejność korzystając z metody select lub z nawiasów kwadratowych, w których zostaną wymienione we właściwej kolejności tylko te kolumny, które mają pozostać.
Przetworzone dane można „zwrócić” zapisując je (write) do pliku. Można zdecydować, że plik ma być nadpisany (mode), oraz wskazać na format tworzonego pliku (parquet).
transformed_df.write.mode("overwrite").parquet('Files/transformed_data/orders')
print ("Transformed data saved!")
Dane zapisane w formacie parquet można wczytać, wskazując na format i lokalizację. Słowo order w ścieżce oznacza nazwę katalogu. W tym katalogu w formacie parquet są przechowywane dane.
Podczas zapisu danych, można je partycjonować. Tutaj we wskazanym w metodzie parquet katalogu powstaną podkatalogi Year i Month wskazujące na konkretną partycję, np. Year=2022/Month=1:
orders_df.write.partitionBy("Year","Month").mode("overwrite").parquet("Files/partitioned_data")
print ("Transformed data saved!")
Następnie w intuicyjny sposób można wczytać tylko część danych:
Jeśli preferujesz pracować z danymi z wykorzystaniem SQL, to możesz zapisać dane „jako tabela” (saveAsTable). Opisanie danych strukturą SQL może być wykonane w wewnętrznym metastore Sparka (tzw managed table) lub może odnosić się do pliku, który znajduje się w data lake (external table). W tej metodzie nie podajemy pełnej ścieżki dostępu do pliku, co oznacza, że będzie on przechowywany w metastore. „delta” to jeden z dostępnych formatów, inne to: csv, parquet, avro itd. Te formaty mogą wprowadzać typowo bazo-danowe funkcjonalności jak transakcje, wersjonowanie rekordów itp.
Mając tak zdefiniowaną tabelę, można pisać do niej zapytania SQL i zapisywać wynik w data frame spark:
df = spark.sql("SELECT * FROM xyz_lakehouse.salesorders LIMIT 1000")
display(df)
Ponieważ spark pozwala na pracę z różnymi językami, to można przełączyć się do języka SQL komendą %%sql
%%sql
SELECT YEAR(OrderDate) AS OrderYear,
SUM((UnitPrice * Quantity) + Tax) AS GrossRevenue
FROM salesorders
GROUP BY YEAR(OrderDate)
ORDER BY OrderYear;
Przełączając się między widokami, można dokonać wstępnej analizy danych na automatycznie generowanych wykresach:
Z drugiej strony Spark to nadal notebook, w którym jest uruchamiany kod Python, dlatego można korzystać z licznych funkcjonalności, które występują w tym języku, np. można tworzyć wykresy z wykorzystaniem popularnego modułu matplotlib:
sqlQuery = "SELECT CAST(YEAR(OrderDate) AS CHAR(4)) AS OrderYear, \
SUM((UnitPrice * Quantity) + Tax) AS GrossRevenue \
FROM salesorders \
GROUP BY CAST(YEAR(OrderDate) AS CHAR(4)) \
ORDER BY OrderYear"
df_spark = spark.sql(sqlQuery)
df_spark.show()
from matplotlib import pyplot as pl
tdf_sales = df_spark.toPandas()
plt.bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'])
plt.show()
Mając pythonowe funkcje możemy wpływać na to, jak taki wykres ma wyglądać:
from matplotlib import pyplot as plt
plt.clf()
plt.bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'], color='orange')
plt.title('Revenue by Year')
plt.xlabel('Year')
plt.ylabel('Revenue')
plt.grid(color='#95a5a6', linestyle='--', linewidth=2, axis='y', alpha=0.7)
plt.xticks(rotation=45)
plt.show()
Matplotlib ma dużo możliwości pod względem formatowania wykresów i generalnie można korzystać ze wszystkich z nich. Tu np. tworzymy dwa wykresy w jednym:
from matplotlib import pyplot as plt
plt.clf()
fig, ax = plt.subplots(1, 2, figsize = (10,4))
ax[0].bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'], color='orange')
ax[0].set_title('Revenue by Year')
yearly_counts = df_sales['OrderYear'].value_counts()
ax[1].pie(yearly_counts)
ax[1].set_title('Orders per Year')
ax[1].legend(yearly_counts.keys().tolist())
fig.suptitle('Sales Data')
plt.show()
Matplotlib jest uniwersalny, ale też pracochłonny. Równolegle powstają też inne biblioteki do tworzenia wykresów, jak np. seaborn: