После того, как мы скачали несколько десятков однотипных файлов, нужно классифицировать их, добавить к ним столбцы, почистить от мусора, потом склеить строки... Здесь мы выбираем наиболее оптимальную структуру папок, структуру имен, содержание служебных файлов... Для этого мы будем использовать функции записи и чтения строк, import CSV, преобразование строк в объект датавремя
Итак, мы скачали несколько десятков (сотен) однотипных таблиц (формат .CSV). Потом сформировали из них (выборку) список файлов. И стали их по очереди обрабатывать. Здесь мы рассмотрим процессы и подберем функции для обработки текстового файла (csv).
In []:
Открываем файл для чтения --------------------------> парсим имя файла => список параметров имени fname[]
\ \
считываем строку => список элементов rawstr[] \ ---> формируем список строк rstr[]
\ \ \ \
"чистим строку" -> rawstr[i] \__________ \ добавляем все строки в новый CSV файл
\ \ /
парсим некоторые элементы строки -> rawstr[i] = rawstri[] ---> собираем новую строку --- записываем строку в новый CSV файл
Очевидно, что поскольку файлы однотипны, достатончо рассмотреть процессы одного файла. Начнем с примера. Библиотека pandas нам далее понадобится только для иллюстративных целей. Дя справки (pandas )можно использовать IPython's Rich Display System
In [2]:
import pandas
In [13]:
%%file data_nissan.csv
"View";"percent";"page"
"13221";"39,38";"http://nissan.ru/catg/nissan/"
"2464";"7,34";"http://nissan.ru/catg/nissan/qashqai/"
"2057";"6,13";"http://nissan.ru/catg/nissan/x-trail/"
"115";"0,34";"http://nissan.ru/catg/nissan/qashqai/2956/1_6_2wd_mt/xe/"
"114";"0,34";"http://nissan.ru/catg/nissan/maxima/"
"90";"0,27";"http://nissan.ru/catg/nissan/gt-r/2678/3_8_550hp_at/premium_edition_-da-a/"
К вопросу о том, куда записался файл. По умолчанию настройки таковы, что все пишется в рабочую директорию. Т.е. в ту, из которой была запущена команда ipython notebook. У меня это C:NotebooksЭто надо помнить... А в модулях использовать абсолютные имена (базовый+ отн. путь).
На самом деле мне пришлось отредактировать первоначальный файл (C:Notebooks), первые четыре строчки выглядели вот так: вторая строка - пустая, вместо запятых точки с запятыми, кавычки... Что со всем этим делать?
In []:
"ниссан.Ru";85837;"http://nissan.ru"
"Просмотры";"%";"Страницы"
"13221";"39,38";"http://nissan.ru/catg/nissan/"
И pandas не хотел их читать и выдавал ошибку. Но даже отредактированный файл прочитался чудом (помогла запятая в среднем столбце).
In [14]:
df = pandas.read_csv('data_nissan.csv')
In [15]:
df
Out[15]:
Хотя мы немного отвлеклись от главной темы, но зато получили иллюстрацию того, как тупой компьютер читает неочищеный файл.
Мы знаем, что текстовые файлы читаются по строкам. А нам нужно добавить в таблицу столбцы. Почему бы вместо того, чтобы считывать строки в цикле и в каждую добавлять в нужное место нужный элемент не использовать какой-нибудь готовый пакет?
Заманчиво... Но сначала ОЧИСТКА - т.е. приведение к каноническому виду...
Знать бы еще, что он есть... Пока не наблюдается... Потому проще написать модули для обработки CSV самому.
Мы знаем, что текстовые файлы читаются по строкам. А нам нужно добавить в таблицу столбцы. Почему бы вместо того, чтобы считывать строки в цикле и в каждую добавлять в нужное место нужный элемент не использовать какой-нибудь готовый пакет?
Заманчиво... Но сначала ОЧИСТКА - т.е. приведение к каноническому виду...
Знать бы еще, что он есть... Пока не наблюдается... Потому проще написать модули для обработки CSV самому.
Открываем файл для чтения¶
Начнем с самого простого - примера из документации13.1. csv — CSV File Reading and Writing
In [16]:
import csv
In [18]:
with open('data_nissan.csv', 'rb') as csvfile:
spamreader = csv.reader(csvfile, delimiter=';', quotechar='"')
for row in spamreader:
print ', '.join(row)
#Spam, Spam, Spam, Spam, Spam, Baked Beans
#Spam, Lovely Spam, Wonderful Spam
Как видно из примера, нужно всего лишь правильно задать разделители и "окавычиватели"... не пишу "кавычки" потому, что в качестве окавычивателей могут использоваться, например "_"
In [39]:
row #Здесь каждаф строка - это список строк
Out[39]:
Можно одной командой считать весь файл. Вот источник примера внизу Reading a specific row & columns of data in a text file using Python 2.7 ...А вот описание атрибута readlines() file.readlines([sizehint])
In [25]:
# read the whole file
file = open("data_nissan.csv", 'r')
lines = file.readlines()
file.close()
# Skip first 2 lines, output the rest to stdout
count = 0
for line in lines:
count +=1
if count > 2:
print line,
In [26]:
lines
Out[26]:
А теперь посмотрим, как обрабатываются файлы с пустыми строками и кирилицей. Прочитаем уже упомянутый выше файл, де первые 4 строки:
In []:
"ниссан.Ru";85837;"http://nissan.ru"
"Просмотры";"%";"Страницы"
"13221";"39,38";"http://nissan.ru/catg/nissan/"
In [28]:
file2 = open("C:\\Users\\kiss\\Documents\\IPython Notebooks\\Nissan\\nissan_ru.csv", 'r')
lines2 = file2.readlines()
file2.close()
lines2
Out[28]:
Как видим, первая и третья строчки кодируются юникодом, а вторая (пустая) существует на общих основаниях... ОчевидноЮ что эти строки можно просто сцепить с новыми ( ... но только слева !!!)
In [37]:
lines2[1]=lines2[1]+"\twow"
lines2[1]
Out[37]:
In [38]:
print lines2[1] #строка будет пропущена и табуляция тоже сработает
Если исходить из того, что не нужно часто обращаться к диску..., то нужно считать все строчки, потом слева сепить их со служебными строчками и все одним махом запиать в новый файл, причем можно писать все в один файл в конец...
Разделители для служебных столбцов сделать такими же, как и для скачаных файлов.
А потом при помощи вызова модуля CSV можно переформатировать разделители...
Разделители для служебных столбцов сделать такими же, как и для скачаных файлов.
А потом при помощи вызова модуля CSV можно переформатировать разделители...
Запись строк в файл¶
Посмотрим, какие опции есть у функции writelines file.writelines([sizehint])
file.writelines(sequence)
Write a sequence of strings to the file. The sequence can be any iterable object producing strings, typically a list of strings. There is no return value. (The name is intended to match readlines(); writelines() does not add line separators.)
Files support the iterator protocol. Each iteration returns the same result as readline(), and iteration ends when the readline() method returns an empty string.
File objects also offer a number of other interesting attributes. These are not required for file-like objects, but should be implemented if they make sense for the particular object.
Write a sequence of strings to the file. The sequence can be any iterable object producing strings, typically a list of strings. There is no return value. (The name is intended to match readlines(); writelines() does not add line separators.)
Files support the iterator protocol. Each iteration returns the same result as readline(), and iteration ends when the readline() method returns an empty string.
File objects also offer a number of other interesting attributes. These are not required for file-like objects, but should be implemented if they make sense for the particular object.
Опций нет, но это список строк. Можно организовать итерации по строкам и слева приклеить строку с данными о файле. Что мне, собственно, и нужно.
Если я получаю csv файл, как результат запроса к некой базе данных, то, естественно, надо перечислит параметры запроса. Их количество может быт разным..., Параметры тоже могут быть разными...
Можно ли в каждую папку класть файл с пояснениями (расшифровкой сокращений)?
Очевидно, что такие файлы не помешают... Вспомним про стандарт csv файла в Weka... Очевидно это придумали математики, а не программисты...
Можно ли в каждую папку класть файл с пояснениями (расшифровкой сокращений)?
Очевидно, что такие файлы не помешают... Вспомним про стандарт csv файла в Weka... Очевидно это придумали математики, а не программисты...
Формат данных для служебного файла¶
А пока подумаем, как лучше размещать информацию в служебном файле. Словарь? Две-три посдлеовательности? dictionaries
Ниже три варианта заполнения словаря, и альтернативный вариант с двумя последовательностями (функция zip())
Ниже три варианта заполнения словаря, и альтернативный вариант с двумя последовательностями (функция zip())
In [40]:
tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
tel
Out[40]:
In [42]:
#The dict() constructor builds dictionaries directly from sequences of key-value pairs:
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
Out[42]:
In [43]:
dict(sape=4139, guido=4127, jack=4098)
Out[43]:
In [41]:
# To loop over two or more sequences at the same time, the entries can be paired with the zip() function.
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
print 'What is your {0}? It is {1}.'.format(q, a)
Элементы служебной строки (параметры файла)¶
In []:
# имя файла - из рассуждений ниже ясно, что для обозначения периода лучше использоват одно поле даты
# Поля между разделителями могут иметь разную длину(?)
#-*-_-*-_-2_-*-*_-*-_-*-_-*-_-*-_-*- - шаблон выберет только файлы с одинаковым полем 2
#-0-_-1-_-2_-3_-4_-5-_-6-_-7-_-8-_-9-
########################################
#0 - projectfile (main project description)
#1 - site (auto.ru)
########################################
#2 - period year(2013)
#3 - period month (12)
#4 - period day (03)
########################################
#5 - statistics source (statauto.ru)
########################################
#6 - weight of sample
#7 - gender (mail)
#8 - age (30-35)
#9 - name (nissan)
Поразмыслим, зачем нам такие имена файлов. Все для того, чтобы вставлять дополнительные служебные столбцы в таблицы.
Мы либо скачиваем готовые таблицы, либо формируем таблицы из html файлов.
Во всех случаях вопрос о том, что именно мы скачали (дата скачивания, период, параметры выборки...) нужно куда-то записывать. Нельзя рассчитывать на то, что их можно будет вытащить из содержания файла.
Мы либо скачиваем готовые таблицы, либо формируем таблицы из html файлов.
Во всех случаях вопрос о том, что именно мы скачали (дата скачивания, период, параметры выборки...) нужно куда-то записывать. Нельзя рассчитывать на то, что их можно будет вытащить из содержания файла.
Поскольку мы организуем сквозной поиск файлов в дереве папок по маске имени, то информация из имен папок теряется.
С другой стороны, поиск можно организовывать из главной папки проекта. И в эту же папку помещать информационные файлы с описанием и расшифровкой сокращений в именах файлов.
С другой стороны, поиск можно организовывать из главной папки проекта. И в эту же папку помещать информационные файлы с описанием и расшифровкой сокращений в именах файлов.
Очевидно, что имя файла с фиксированным количеством разелителей, например "_" дает хорошие возможности для маскирования. Если мы еще прием моглашение о пустые подстроки-параметры обозначаются "--", то мы сможем задавать что-нибудь вроде:
In []:
# auto_mstat_2013-12-01_-3_-4_-5-_-6-_-7-_-8-_-9-
О формате даты¶
Отдельно стоит поговорить о формате даты. Пожалуй, это ошибка - пытаться выделить три столбца под год,месяц, день. Есть еще неделя и день недели. Так что, правильнее задавать строку даты так, чтобы ее можно было легко конвертировать в формат даты внутри таблицы.
Для этого, полагаю есть сооветствующие конвертеры: date, datetime, and time objects all support a strftime(format) method, to create a string representing the time.
А если мы захотим отфильтровать файлы по маске имени, то нам без разницы, какой разделитель (подчерк или дефис), лишь бы он был.
Для этого, полагаю есть сооветствующие конвертеры: date, datetime, and time objects all support a strftime(format) method, to create a string representing the time.
А если мы захотим отфильтровать файлы по маске имени, то нам без разницы, какой разделитель (подчерк или дефис), лишь бы он был.
In [48]:
from datetime import datetime
date_object = datetime.strptime('Jun 1 2005 1:33PM', '%b %d %Y %I:%M%p')
Слева строка, а справа строка форматирования (таблица символов чуть ниже). Вот строчка, которую я сконструировал, её можно использовать в имени файла:
In [62]:
date_object1=datetime.strptime('12-31-2013', '%m-%d-%Y')
date_object1
Out[62]:
Directive | Meaning | Example | Notes |
---|---|---|---|
%a | Weekday as locale’s abbreviated name. |
Sun, Mon, ..., Sat (en_US);
So, Mo, ..., Sa (de_DE)
|
(1) |
%A | Weekday as locale’s full name. |
Sunday, Monday, ..., Saturday (en_US);
Sonntag, Montag, ..., Samstag (de_DE)
|
(1) |
%w | Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. | 0, 1, ..., 6 | |
%d | Day of the month as a zero-padded decimal number. | 01, 02, ..., 31 | |
%b | Month as locale’s abbreviated name. |
Jan, Feb, ..., Dec (en_US);
Jan, Feb, ..., Dez (de_DE)
|
(1) |
%B | Month as locale’s full name. |
January, February, ..., December (en_US);
Januar, Februar, ..., Dezember (de_DE)
|
(1) |
%m | Month as a zero-padded decimal number. | 01, 02, ..., 12 | |
%y | Year without century as a zero-padded decimal number. | 00, 01, ..., 99 | |
%Y | Year with century as a decimal number. | 1970, 1988, 2001, 2013 | |
%H | Hour (24-hour clock) as a zero-padded decimal number. | 00, 01, ..., 23 | |
%I | Hour (12-hour clock) as a zero-padded decimal number. | 01, 02, ..., 12 | |
%p | Locale’s equivalent of either AM or PM. |
AM, PM (en_US);
am, pm (de_DE)
|
(1), (2) |
%M | Minute as a zero-padded decimal number. | 00, 01, ..., 59 | |
%S | Second as a zero-padded decimal number. | 00, 01, ..., 59 | (3) |
%f | Microsecond as a decimal number, zero-padded on the left. | 000000, 000001, ..., 999999 | (4) |
%z | UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive). | (empty), +0000, -0400, +1030 | (5) |
%Z | Time zone name (empty string if the object is naive). | (empty), UTC, EST, CST | |
%j | Day of the year as a zero-padded decimal number. | 001, 002, ..., 366 | |
%U | Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0. | 00, 01, ..., 53 | (6) |
%W | Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0. | 00, 01, ..., 53 | (6) |
%c | Locale’s appropriate date and time representation. |
Tue Aug 16 21:30:00 1988 (en_US);
Di 16 Aug 21:30:00 1988 (de_DE)
|
(1) |
%x | Locale’s appropriate date representation. |
08/16/88 (None);
08/16/1988 (en_US);
16.08.1988 (de_DE)
|
(1) |
%X | Locale’s appropriate time representation. |
21:30:00 (en_US);
21:30:00 (de_DE)
|
(1) |
%% | A literal '%' character. | % |
In [54]:
date_object.isoformat()
Out[54]:
In [56]:
date_object.month
Out[56]:
Пока остановимся на том, что просмотр файлов в папке будет проще, если мы используем формат год-месяц-день. И, естественно, нельзя забывать о том, что поле дата - это не дата, а, как правило, период (год, месяц, неделя, день) за который собраны данные в файле.
Продолжаем рассуждения об имени файла¶
In []:
# auto_mstat_2013-12-01_cat-nissan_-4_-5-_-6-_-7-_-8-_-9-
Итак, с превыми тремя полями определились, а что дальше? Дальше параметры (например, пол, возраст..., марка авто). Их надо бы указывать точнее, например, при формировании выборки из лога сервера для файла "nissan" можно фильтровать в логе по слову "nissan", а можно по "cat/nissan"... но для таблиц нужна будет строчка "nissan". Чтобы разрешить это противоречие можно использовать словарь. Поскольку словари нам понадобятся и для дальнейшей обработки строк (в частности, при парсинге URL), то есть смысл их использовать и здесь. О словарях поговорим в следующих постах, а здесь только попробуем:
In []:
dirinfo={'cat-nissan':'nissan','auto':'auto.ru','mstat':'mstat.ru',.....,'2013-12-01':'2013-12-01'}
Наверное проще записывать в словарь все параметры строки (см. последний элемент в словаре), даже, если мы не собираемся переопредлеять строку.
Где хранить словарь? Очевидно, в главной папке проекта. Нарисовал картинку внизу... Напрашивается нетривиальный вывод. Главная папка - это подпапка период в Папке "auto"... сайт идентифицируется в имени файла, счетчик - это второй параметр, и выборок с разных сайтов я могу настрогать довольно много ... но сравнивать я могу только выборки за равные промежутки (месяцы с месяцами, а не с днями...)
In []:
сайт2 (autoclub.ru)
/ \
счетчик1 Счетчик2 (mstat) ----> посещаемость за период (y,m,w,d,...)
\ /
сайт1(auto.ru)
А если за месяц счетчик 1 - мужчины, а счетчик 2 - все до 35 лет... (мягкое с желтым)... первостепенными становятся не сайты, а параметры... Например, если сформировать выборки с учетом возраста или пола, то надо дополнительно рассчитывать веса при склейке файлов... Это требует разной обработки..., очевидно, что и папки должны быть разными
In []:
auto with gender (you have to use weights) src (сайт1+счетчик1)
\ / /
period = (month) -----------------------------> without age and gender -----> src(сайт1+счетчик2)
\
with age (you have to use weights)
\\\\\\\\
src (18) (25 35 40
Не ясно только что во что вкладывать - папки счетчиков в папки сайтов, или наоборот... И то и другое влияет на обработку, потому проще называть папки с учетоа сайта и счетчика, в каждую папку помещать информационные файлы (манифесты) - (сайт№+счетчик№)... их не так много получится Информационные файлы все текстовые... должны иметь одинаковый элемент, чтобы можно было все их посмотреть при помощи сквозного поиска.
In []:
pro_ject.log# описание проекта
# Сайт:
# Счетчик:
# Параметры:
# Обработка: нужно сфрмировать столбец весов, учитывающий пол и возраст
# Сформирован сборный файл
# Удалить исходники ?
pro_dict.py # импортируемый модуль со словарями
Ну и... что мы в итоге получим? Суммарные файлы таблиц (сайт№+счетчик№)... Эти файлы иногда тоже надо будет склеивать... Но это штучные операции. Для них нужны инструменты для перестановки и переименования столбцов ... и склейки строк файлов... Поскольку есть бесплатные редакторы таблиц и, мы эти операции пока не планируем... а потом нужно будет использовать возможности R..., Rapid Miner...
Комментариев нет:
Отправить комментарий