diff --git a/sql.html b/sql.html new file mode 100644 index 0000000..5ee8d48 --- /dev/null +++ b/sql.html @@ -0,0 +1,15271 @@ + + +
+ + +ID c параметрами INTEGER PRIMARY KEY AUTOINCREMENT.import sqlite3
+import os
+DATABASE_NAME = r'works.sqlite'
+if os.path.isfile(DATABASE_NAME):
+ os.remove(DATABASE_NAME)
+con = sqlite3.connect(DATABASE_NAME)
+query = r"""CREATE TABLE IF NOT EXISTS works (ID INTEGER PRIMARY KEY AUTOINCREMENT,
+salary INTEGER, educationType TEXT, jobTitle TEXT, qualification TEXT, gender TEXT, dateModify DATE, skills TEXT,
+otherInfo TEXT);"""
+cur = con.cursor()
+cur.execute("DROP TABLE IF EXISTS works")
+cur.execute(query)
+<sqlite3.Cursor at 0x1c512791a40>+
cur.execute("pragma table_info(works)").fetchall()
+[(0, 'ID', 'INTEGER', 0, None, 1), + (1, 'salary', 'INTEGER', 0, None, 0), + (2, 'educationType', 'TEXT', 0, None, 0), + (3, 'jobTitle', 'TEXT', 0, None, 0), + (4, 'qualification', 'TEXT', 0, None, 0), + (5, 'gender', 'TEXT', 0, None, 0), + (6, 'dateModify', 'DATE', 0, None, 0), + (7, 'skills', 'TEXT', 0, None, 0), + (8, 'otherInfo', 'TEXT', 0, None, 0)]+
import pandas as pd
+df = pd.read_csv("works.csv")
+df.to_sql('works', con, if_exists="append", index=False)
+cur.execute("SELECT * FROM works LIMIT 5").fetchall()
+[(1, + 60000, + 'Высшее', + 'Специалист пресс-службы', + 'Магистр', + 'Мужской', + '2021-04-01', + '<p>Аналитическое мышление, <span class="s6"><span class="bumpedFont15">ответственность, </span></span><span class="s6"><span class="bumpedFont15">стабильность психологического состояния и настроения. </span></span></p>', + None), + (2, + 85000, + 'Высшее', + 'менеджер проектов', + None, + 'Мужской', + '2021-04-01', + None, + None), + (3, + 15000, + 'Среднее профессиональное', + '....', + None, + 'Женский', + '2021-06-01', + None, + None), + (4, 30000, None, None, None, 'Женский', '2021-04-03', None, None), + (5, 45000, None, None, None, 'Мужской', '2021-06-28', None, None)]+
salary. Изменится ли после этого размер файла? На сколько?import os.path
+sixe_before_index = os.path.getsize(DATABASE_NAME)
+print(f"Размер до создания индекса: {sixe_before_index} байт(а).")
+Размер до создания индекса: 6500352 байт(а). ++
cur.execute("CREATE INDEX salary_index on works (salary)")
+<sqlite3.Cursor at 0x1c512791a40>+
size_after_index = os.path.getsize(DATABASE_NAME)
+print(f"Размер после создания индекса: {size_after_index} байт(а).")
+print(f"Разница: {size_after_index - sixe_before_index} байт(а).")
+Размер после создания индекса: 6844416 байт(а). +Разница: 344064 байт(а). ++
row_count = cur.execute("SELECT COUNT(*) FROM works").fetchone()
+print(row_count[0])
+32683 ++
genders = ("Мужской", "Женский")
+
+for gender in genders:
+ count = cur.execute(f"SELECT COUNT(*) FROM works WHERE {gender=}").fetchone()
+ print(f"Количество людей с гендором '{gender}': {count[0]}")
+Количество людей с гендором 'Мужской': 13386 +Количество людей с гендором 'Женский': 17910 ++
skills_count = cur.execute("SELECT COUNT(*) FROM works WHERE skills IS NOT NULL").fetchone()
+print(f"Количество заполненных скилов: {skills_count[0]}.")
+Количество заполненных скилов: 8972. ++
skills = cur.execute("SELECT skills FROM works WHERE skills IS NOT NULL").fetchall()
+skills = [skls[0] for skls in skills]
+print(*skills[:5], sep="\n\n")
+<p>Аналитическое мышление, <span class="s6"><span class="bumpedFont15">ответственность, </span></span><span class="s6"><span class="bumpedFont15">стабильность психологического состояния и настроения. </span></span></p> + +Ответственная,пунктуальная,дисциплинированная я,стрессоустойчивая,легкообучаема ,технически оснащена + +<p>Коммуникабельность </p> + +<p>Ответственность в работе</p> + +<p>Усидчивость, умение удерживать в памяти нужную информацию,скрупулезность, пунктуальность, умение планировать и организовать рабочий процесс,дальновидность, умение собирать и анализировать данные, аккуратность, нацеленность на результат.</p> ++
query = "SELECT salary FROM works WHERE lower(skills) LIKE '%python%' OR lower(skills) LIKE '%питон%'"
+python_salaries = cur.execute(query).fetchall()
+python_salaries = [slr[0] for slr in python_salaries]
+print(*python_salaries[:5], sep="\n\n")
+35000 + +20000 + +35000 + +15000 + +25000 ++
import numpy as np
+
+q = [i/10 for i in range(1, 11)]
+for gender in genders:
+ query = f"SELECT salary FROM works WHERE {gender=}"
+ salaries = [slr[0] for slr in cur.execute(query).fetchall()]
+ res = np.quantile(np.array(salaries), q)
+ print(f"{gender=}")
+ print(*[f"{i} -> {j}" for i, j in zip(q, res)], sep="\n")
+gender='Мужской' +0.1 -> 15000.0 +0.2 -> 20000.0 +0.3 -> 25000.0 +0.4 -> 30000.0 +0.5 -> 30000.0 +0.6 -> 35000.0 +0.7 -> 40000.0 +0.8 -> 50000.0 +0.9 -> 60000.0 +1.0 -> 1000000.0 +gender='Женский' +0.1 -> 15000.0 +0.2 -> 18000.0 +0.3 -> 20000.0 +0.4 -> 22000.0 +0.5 -> 25000.0 +0.6 -> 30000.0 +0.7 -> 30000.0 +0.8 -> 35000.0 +0.9 -> 47000.0 +1.0 -> 900000.0 ++
import matplotlib.pyplot as plt
+
+educations = cur.execute("SELECT DISTINCT educationType FROM works WHERE educationType IS NOT NULL").fetchall()
+educations = [ed[0] for ed in educations]
+
+for gender in genders:
+ for educationType in educations:
+ plt.figure(figsize=(10, 5))
+ plt.grid()
+ plt.title(f"Распределение по заработной плате для пол : '{gender}' с образованием: '{educationType}'")
+ plt.xlabel("Зарплата")
+ plt.ylabel("Количество")
+ query = f'SELECT salary FROM works WHERE {gender=} and {educationType=}'
+ salaries = [i[0] for i in cur.execute(query).fetchall()]
+ plt.hist(salaries, bins=40)
+
+ДЗ
+Выделим отдельные сущности:
+Создайте отдельную таблицу с гендером, заполните ее значениями, сделайте на нее внешний ключ из таблицы works.
+Отдельная таблица для образования.
+ +cur.execute("DROP TABLE IF EXISTS genders")
+cur.execute("CREATE TABLE IF NOT EXISTS genders (ID INTEGER PRIMARY KEY AUTOINCREMENT, gender TEXT)")
+cur.executemany("INSERT INTO genders(gender) VALUES (?);", [(g, ) for g in genders])
+cur.execute("DROP TABLE IF EXISTS educations")
+cur.execute("CREATE TABLE IF NOT EXISTS educations (ID INTEGER PRIMARY KEY AUTOINCREMENT, education TEXT)")
+cur.executemany("INSERT INTO educations(education) VALUES (?);", [(e, ) for e in educations])
+con.commit()
+table_data = cur.execute("SELECT * FROM works").fetchall()
+cur.execute("DROP TABLE IF EXISTS works")
+
+genders_converter = {j:i + 1 for i, j in enumerate(genders)}
+educations_converter = {j: i + 1 for i, j in enumerate(educations)}
+
+query = r"""CREATE TABLE IF NOT EXISTS works (ID INTEGER PRIMARY KEY AUTOINCREMENT,
+salary INTEGER, educationType INTEGER, jobTitle TEXT, qualification TEXT , gender INTEGER, dateModify DATE, skills TEXT,
+otherInfo TEXT, FOREIGN KEY (educationType) REFERENCES educations (ID), FOREIGN KEY (gender) REFERENCES genders (ID));"""
+cur.execute(query)
+
+data_with_refs = []
+for row in table_data:
+ edu = educations_converter.get(row[2])
+ gender = genders_converter.get(row[5])
+ data_with_refs.append((*row[:2], edu, *row[3:5], gender, *row[6:]))
+
+cur.executemany("INSERT INTO works VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", data_with_refs)
+con.commit()
+Скилы и otherInfo. Очистите эти поля от HTML.
+ +import re
+
+CLEANR = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
+data_to_clear = cur.execute("SELECT ID, skills, otherInfo from works").fetchall()
+cleared_data = []
+for i, skill, other_info in data_to_clear:
+ if skill is not None:
+ skill = re.sub(CLEANR, '', skill)
+ if other_info is not None:
+ other_info = re.sub(CLEANR, '', other_info)
+ cleared_data.append((skill, other_info, i))
+cur.executemany("UPDATE works SET skills = ?, otherInfo = ? WHERE ID = ?", cleared_data)
+con.commit()
+
+
+Аналитическое мышление, ответственность, стабильность психологического состояния и настроения.
',\n", + " None),\n", + " (2,\n", + " 85000,\n", + " 'Высшее',\n", + " 'менеджер проектов',\n", + " None,\n", + " 'Мужской',\n", + " '2021-04-01',\n", + " None,\n", + " None),\n", + " (3,\n", + " 15000,\n", + " 'Среднее профессиональное',\n", + " '....',\n", + " None,\n", + " 'Женский',\n", + " '2021-06-01',\n", + " None,\n", + " None),\n", + " (4, 30000, None, None, None, 'Женский', '2021-04-03', None, None),\n", + " (5, 45000, None, None, None, 'Мужской', '2021-06-28', None, None)]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cur.execute(\"SELECT * FROM works LIMIT 5\").fetchall()" + ] + }, + { + "cell_type": "markdown", + "id": "94bd8ce7", + "metadata": {}, + "source": [ + "2. Добавьте индекс на поле `salary`. Изменится ли после этого размер файла? На сколько?" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d655925a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Размер до создания индекса: 6500352 байт(а).\n" + ] + } + ], + "source": [ + "import os.path\n", + "sixe_before_index = os.path.getsize(DATABASE_NAME)\n", + "print(f\"Размер до создания индекса: {sixe_before_index} байт(а).\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "38e33ce0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Аналитическое мышление, ответственность, стабильность психологического состояния и настроения.
\n", + "\n", + "Ответственная,пунктуальная,дисциплинированная я,стрессоустойчивая,легкообучаема ,технически оснащена\n", + "\n", + "Коммуникабельность
\n", + "\n", + "Ответственность в работе
\n", + "\n", + "Усидчивость, умение удерживать в памяти нужную информацию,скрупулезность, пунктуальность, умение планировать и организовать рабочий процесс,дальновидность, умение собирать и анализировать данные, аккуратность, нацеленность на результат.
\n" + ] + } + ], + "source": [ + "skills = cur.execute(\"SELECT skills FROM works WHERE skills IS NOT NULL\").fetchall()\n", + "skills = [skls[0] for skls in skills]\n", + "print(*skills[:5], sep=\"\\n\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "711df366", + "metadata": {}, + "source": [ + "7. Вывести зарплату только у тех, у кого в скилах есть Python." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4262aa59", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "35000\n", + "\n", + "20000\n", + "\n", + "35000\n", + "\n", + "15000\n", + "\n", + "25000\n" + ] + } + ], + "source": [ + "query = \"SELECT salary FROM works WHERE lower(skills) LIKE '%python%' OR lower(skills) LIKE '%питон%'\"\n", + "python_salaries = cur.execute(query).fetchall()\n", + "python_salaries = [slr[0] for slr in python_salaries]\n", + "print(*python_salaries[:5], sep=\"\\n\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "353ca7f3", + "metadata": {}, + "source": [ + "8. Построить перцентили и разброс по з/п у мужчин и женщин." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "85afdee1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gender='Мужской'\n", + "0.1 -> 15000.0\n", + "0.2 -> 20000.0\n", + "0.3 -> 25000.0\n", + "0.4 -> 30000.0\n", + "0.5 -> 30000.0\n", + "0.6 -> 35000.0\n", + "0.7 -> 40000.0\n", + "0.8 -> 50000.0\n", + "0.9 -> 60000.0\n", + "1.0 -> 1000000.0\n", + "gender='Женский'\n", + "0.1 -> 15000.0\n", + "0.2 -> 18000.0\n", + "0.3 -> 20000.0\n", + "0.4 -> 22000.0\n", + "0.5 -> 25000.0\n", + "0.6 -> 30000.0\n", + "0.7 -> 30000.0\n", + "0.8 -> 35000.0\n", + "0.9 -> 47000.0\n", + "1.0 -> 900000.0\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "q = [i/10 for i in range(1, 11)]\n", + "for gender in genders:\n", + " query = f\"SELECT salary FROM works WHERE {gender=}\"\n", + " salaries = [slr[0] for slr in cur.execute(query).fetchall()]\n", + " res = np.quantile(np.array(salaries), q)\n", + " print(f\"{gender=}\")\n", + " print(*[f\"{i} -> {j}\" for i, j in zip(q, res)], sep=\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "0e75c55f", + "metadata": {}, + "source": [ + "9. Построить графики распределения по з/п мужчин и женщин (а также в зависимости от высшего образования)." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "58c5d0c7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "