Страницы: 1
RSS
Обработка импортированных из csv данных
 
Доброго всем дня!
Прошу помощи в решении следующей задачи.
Есть файл report.csv, в который программой методом добавления к существующим записываются почасовые данные - показания электросчетчиков.
В другом файле "Показания счетчиков" ведется учет ежемесячных показаний этих счетчиков.
Сейчас настроено так: в файл "Показания счетчиков" импортируются ВСЕ данные из "report.csv", а затем вручную показания за последний опрос копируются на лист "данные", в соответствии с номером счетчика.
Основная проблема при этом, что из-за ненужной информации раздувается файл "Показания счетчиков", а также в том, что присутствует ручная выборка данных.
Я хочу настроить импорт из "report.csv" не всех показаний, а только за последний по времени опрос, а в идеале и запись их в нужные ячейки макросом.

Дополнительная информация  - в файле "Показания счетчиков".
 
Интересная задача... Для получения результата "в идеале" достаточно написать запрос, который будет по требованию вытаскивать из csv файла последние по времени записи по заданному набору счетчиков, тогда можно будет обойтись без доп.листа. Вы что-то сами пробовали делать? Пока что сейчас все нужно с 0 делать.
 
Я попытался сначала сделать автоимпорт данных из csv макросом, что-то не пошло, знаний не хватило. Поэтому сделал его штатными методами excell, но естественно, получился импорт всех данных. В "макросописании" я новичек, и максимум, что смогу - разобрать "по косточкам" существующий код и попытаться понять, что к чему...
 
тогда надо читать файл csv , например,  с помощью Line Input и сравнивать с последним значением времени (часа) опроса - как только находим большее, то записываем его куда надо  
Живи и дай жить..
 
Вы не указали что такое "последний опрос" в таблице csv такого критерия нет.
Работать надо не 12 часов, а головой.
 
Я основную проблему вижу в датах опроса. Нет проблем найти последний опрос - но не факт что у всех последний будет именно в один день.
Намного проще найти данные по конкретному счётчику за конкретный день. Или не найти. т.е. если Вы знаете день опроса - пишите его в таблицу, выделяете - выполняете макрос, он заполняет таблицу по счётчикам, если есть данные в csv. Это как вариант.
Такой код простой, без головоломки. Так годится?
Только код писать сейчас некогда...
 
Я думаю, что вторую часть задачи можно решить немного проще: не обязательно макросу проверять соответствие номера счетчика при вписывании показаний на лист "данные". Если на листе "Импорт_данных" будут только актуальные данные, а также структура показаний на нем будет совпадать со структурой на листе "данные", то можно просто копировать и вставлять целым блоком
 
Блоки на листе "Данные" у Вас всегда одинаковы? И Игорь правильно заметил проблему - не по всем счетчикам могут быть данные за указанный день. Как быть в таком случае? Пропускать показатели?
 
Цитата
Leanna написал: Вы не указали что такое "последний опрос" в таблице csv такого критерия нет.
>Опросы ежечасные. Количество опрошенных счетчиков может меняться (т.к. счетчик может быть отключен в момент опроса, и данные о нем не будут отражены). Но внутри одного опроса счетчик не может быть опрошен более одного раза. Порядок опроса (т.е. порядок записей по счетчикам внутри опроса) не меняется.

Тогда если смотреть с конца таблицы csv и вверх (записи добавляются в конец), то к предыдущему опросу отнесем запись, удовлетворяющую условиям:
1. Повтор номера счетчика (внутри одного опроса счетчик не может быть опрошен более одного раза)
или
2. Время опроса не принадлежит часу последнего опроса (если последняя строка со временем 10.03.2015  08:00:34, то все, что раньше 10.03.2015  08:00:00 -  это предыдущий, ненужный, опрос)
 
Цитата
Hugo написал: не факт что у всех последний будет именно в один день
Да, такая ситуация теоретически возможна.
Цитата
Hugo написал:
Намного проще найти данные по конкретному счётчику за конкретный день.
Да, конечно, этот вариант абсолютно устроил бы. Что касается времени, то можно было бы выбирать записи с максимальным значением времени в заданной дате.
 
Я вижу примерно такой алгоритм - читаем файл как текст построчно, без записи на лист, запоминаем в словаре данные счётчика по ключу номер|дата - в словаре останутся последние данные за день. Если идти по файлу снизу - можно запомнить первые встреченные, не важно как реализовывать. Можно просто запомнить максимальные показания счётчика за конкретный день.
Теперь если известен номер и дата - из словаря берём данные (если есть).
Т.е. сперва прочитали csv, затем циклом по списку счётчиков в таблице по известной дате разложили данные. Или циклом в цикле по списку счётчиков и списку дат.
Изменено: Hugo - 10.03.2015 13:43:45 (описька...)
 
а нельзя чтоли сделать запрос к CSV файлу через SQL? SELECT то вроде не сложный
Учимся сами и помогаем другим...
 
Добрый день
Написал на скорую руку:
Скрытый текст
 
Цитата
Влад написал:Блоки на листе "Данные" у Вас всегда одинаковы?... не по всем счетчикам могут быть данные за указанный день.... Пропускать показатели?
Да, блоки  на листе "Данные" всегда одинаковые.
Если нет данных, пропускать показатели.
 
Моя заготовка, на что времени хватило, для начала:
Код
Sub tt()
    Dim a, b, i&, t$
    a = Split(CreateObject("Scripting.FileSystemObject").Getfile("C:\Downloads\elektro\report.csv").OpenasTextStream(1).ReadAll, vbNewLine)
    With CreateObject("Scripting.Dictionary"): .comparemode = 1
        For i = 0 To UBound(a)
            If Len(a(i)) Then
                b = Split(a(i), ",")
                t = b(1) & "|" & Split(b(0))(0)
                .Item(t) = b(6)
            End If
        Next
        
        'словарь заполнен, можно извлекать
        t = "19801625|10.03.2015"
        MsgBox .Item(t)

    End With
End Sub

 
Спасибо, буду разбираться.
С точки зрения "изящности" решения, вариант без записи на лист конечно, лучше. Но для меня он сложнее в понимании на порядок. Если смогу решить эту задачку, буду это делать поэтапно. Главной проблемой считаю импорт данных только последнего опроса, в эту сторону и буду пока рыть
 
Ну для понимания можно в моём варианте выгрузить собранное на лист. Чисто для информации.
А в процессе отладки содержимое массива можно смотреть в окне Locals, а для словаря можно дописать в код пару строк:
Код
        'словарь заполнен, можно извлекать
        ключи = .keys
        значения = .items
        Stop ' и смотрим что в словаре (начало словаря) в окне Locals
        t = "19801625|10.03.2015"
        MsgBox t & "=" & .Item(t)

 
Вот рабочий код в модуль листа "данные" - просто даблкликнуть ячейку с датой в третьей строке:
Код
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
    Dim a, b, sPath$, i&, il&, t$, d$
    If Target.Row <> 3 Then Exit Sub
    If IsDate(Selection.Value) Then
        Application.ScreenUpdating = False
        Cancel = True
        sPath = ThisWorkbook.Path & "\report.csv"
        a = Split(CreateObject("Scripting.FileSystemObject").GetFile(sPath).OpenAsTextStream(1).ReadAll, vbNewLine)
        With CreateObject("Scripting.Dictionary"): .comparemode = 1
            For i = 0 To UBound(a)
                If Len(a(i)) Then
                    b = Split(a(i), ",")
                    t = b(1) & "|" & Split(b(0))(0)
                    .Item(t) = b(6)
                End If
            Next

            il = Range("E" & Rows.Count).End(xlUp).Row
            d = Format(Target.Value, "dd.mm.yyyy")
            For i = 13 To il
                t = Cells(i, 5) & "|" & d
                If .exists(t) Then Cells(i, Target.Column).Value = Int(.Item(t))
            Next
        End With
        Erase a, b
        Application.ScreenUpdating = True
    End If
End Sub
 
Цитата
Hugo написал: Ну для понимания можно в моём варианте выгрузить собранное на лист. Чисто для информации.
А в процессе отладки содержимое массива можно смотреть в окне Locals, а для словаря можно дописать в код пару строк:  
ФАНТАСТИКА! Так гораздо удобнее разбираться в коде.

Цитата
Hugo написал: Вот рабочий код в модуль листа "данные" - просто даблкликнуть ячейку с датой в третьей строке    
Большое спасибо! Этот код вообще пока за гранью для меня, но я его расшифрую;) не без помощи форумчан.
Кстати, у меня он выдал ошибку Run-time error '13':Type mismatch при выполнении строки
Код
If .exists(t) Then Cells(i, Target.Column).Value = Int(.Item(t))
Хочу самостоятельно проанализировать код и найти причину ошибки. Если не получится, обращусь к сообществу. Всем огромное спасибо за помощь!
 
Цитата
Alexander88 написал: Написал на скорую руку
Только сейчас увидел Ваш код. Спасибо!
 
Ошибка из-за разделителя, у меня дома тоже также... Исправил, см. архив. Даблкликать по жёлтым ячейкам, да вообще по любым можно.
 
Сейчас разбираю Ваш макрос с ошибкой по косточкам:) Оказывается, это может быть увлекательным занятием.
Самому любопытно - подгляжу в ответ "ребус-кроссворда" или справлюсь самостоятельно 8)
Изменено: Александр - 11.03.2015 00:13:17
 
Сейчас только заметил - там в строке
Код
If IsDate(Selection.Value) Then
нужно бы заменить Selection на Target - это от первой версии хвост остался. Хотя и не мешает, т.к. по факту это будет одна ячейка.
 
И еще раз всем здравствуйте!
Немного подточил код макроса от ув. Hugo под себя.
В процессе изучения кода у меня появилось несколько вопросов:
1. Что за параметр Cancel в функции Worksheet_BeforeDoubleClick, за что он отвечает?
2. Выражение
Код
sep_ = Mid(1 / 2, 2, 1)
для меня осталось загадкой. Вместо неё для замены точки на запятую в данных я использовал конструкцию
Код
If .exists(t) Then Cells(i, Target.Column).Value = Int(Replace(.Item(t), ".", ","))
и она работает. В ней есть подводные камни?
3. Когда я для очистки ячеек (в случаях, когда данные в словаре не найдены) попробовал вписать после кода
Код
For i = 13 To il
 t = Cells(i, 5) & "|" & d
 If .exists(t) Then Cells(i, Target.Column).Value = Int(Replace(.Item(t), ".", ","))  
строку
Код
Else .exists(t) Then Cells(i, Target.Column).Value = ""
то Excell пожаловался на отсутствие If (что-то типа Else  без If). Видимо, потому, что If в данном случае работает как-то по-другому? Просветите, пожалуйста:oops:
У меня сработала такая строка, и я её оставил вместо предыдущей:
Код
If Not .exists(t) Then Cells(i, Target.Column).Value = "" 
4. И еще мне не понятно, как макросом определяется максимальное значение показания счетчика в конкретной дате? Предположу, что кодом наполнения словаря
Код
With CreateObject("Scripting.Dictionary"): .comparemode = 1
                  For i = 0 To UBound(a)
                If Len(a(i)) Then 
                    b = Split(a(i), ",")
                    t = b(1) & "|" & Split(b(0))(0) 
                 .Item(t) = b(6) 
идет проверка строк сверху вниз методом замены предыдущего найденного значения последующим. А т.к. последующее - более свежее показание, то оно и остается в словаре, закрепляясь за ключем. Я прав?
5. В строке
Код
t = b(1) & "|" & Split(b(0))(0)   
Split(b(0))(0) - это дата. Выскажу предположение по поводу её извлечения здесь: функция Split(b(0))(0) разбивает переменную b(0) "дата время" на две ПОДпеременные, разделенные пробелом (аргумент Delimiter опущен) и выбирает первую подпеременную Дата, т.к. указан ноль (0) после Split(b(0)). Во завернул :) Это где-то рядом с действительностью или  я  в корне неправильно понял? Сразу прошу прощения у форумчан за дилетанские высказывания, а у экселя за новообразование "ПОДпеременные" :D Ну не знаю я, как это называется...
Изменено: Александр - 12.03.2015 09:08:20 (На первый вопрос нашел ответ)
 
Цитата
Александр написал: Что за параметр Cancel в функции Worksheet_BeforeDoubleClick, за что он отвечает?
Сори, нашел инфу по этому вопросу. Для тех, кто не знает и для себя, если забуду :D:
Cancel = True  - это отмена стандарного поведение Excell по двойному клику в заданном диапазоне. Требуется, чтобы назначить свои действия.
Изменено: Александр - 11.03.2015 20:26:47
Страницы: 1
Наверх