How to interpolate zero cells in a large excel sheet? - excel

I have a long excel file which contains numbers collected from a website. Less than 1% of the cells contain zero due to an error from the source. Therefore, I want to find update those cells and interpolate them with the nearest values. The length of the zero cells is some time single, hence I can simply take the average of the nearest non-zero values. However, a few places it is longer than one, hence I need to use linear interpolation.
Sample extracted data
+---+------+------+------+------+------+------+------+------+---+---+---+---+---+---+---+---+------+------+------+------+------+------+------+------+------+
| | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y |
+---+------+------+------+------+------+------+------+------+---+---+---+---+---+---+---+---+------+------+------+------+------+------+------+------+------+
| 1 | 4058 | 4048 | 4049 | 4082 | 4090 | 4115 | 4118 | 4109 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3990 | 4058 | 4064 | 4053 | 4057 | 4093 | 4123 | 4137 | 4133 |
+---+------+------+------+------+------+------+------+------+---+---+---+---+---+---+---+---+------+------+------+------+------+------+------+------+------+

Here's a very general script that might do something like that. It is only tested on positive values and ten rows, so you will definitely need to adapt it to many corner cases - but it should point you in the right direction:
Sub Interpolate()
Dim valueToTop As Integer
For Row = 1 To 10
valueToTop = -1
valueToBottom = -1
If Cells(Row, 1).Value = 0 Then
RowToTop = Row - 1
Do While RowToTop > 0
If Cells(RowToTop, 1).Value > 0 Then
valueToTop = Cells(RowToTop, 1)
Exit Do
End If
RowToTop = RowToTop - 1
Loop
Debug.Print valueToTop
Debug.Print RowToTop
RowToBottom = Row + 1
Do While RowToBottom > 0
If Cells(RowToBottom, 1).Value > 0 Then
valueToBottom = Cells(RowToBottom, 1)
Exit Do
End If
RowToBottom = RowToBottom + 1
Loop
Debug.Print valueToBottom
Debug.Print RowToBottom
Cells(Row, 2).Value = valueToTop + (Row - RowToTop) * (valueToBottom - valueToTop) / (RowToBottom - RowToTop)
End If
Next Row
End Sub

Related

Date diff between many rows in Excel

I have USER_ID, EVENT_ID and DATE and I need to check which of the events are real.
Real event is the first one by each user and every one which is at least 10 days after the last real event.
What i do is to order them by USER_ID and date and using this forumla =IF(A2<>A1;1;IF(C2-C1>=10;1;0)) but that way it calculates date diff between current and the previous event, not to the last real event.
Like if i have events at 01 05 and 13 from one user it will give me 1, 0, 0 but i need to compare to the last real event which is at 01 and it should give me 1, 0 ,1.
How can i do that.
| USER ID | EVENT ID | DATE | What I get | What I should get |
|:---------|:---------:|:------------:| :------------:|:------------: |
| 1 | 4 | 01.01.2020 | 1 | 1 |
| 1 | 5 | 05.01.2020 | 0 | 0 |
| 1 | 6 | 13.01.2020 | 0 | 1 |
| 2 | 7 | 03.01.2020 | 1 | 1 |
| 2 | 8 | 05.01.2020 | 0 | 0 |
| 2 | 9 | 06.01.2020 | 0 | 0 |
The number of events by user is not fixed.
Table
Here's a solution in VBA.
Sub Button1_Click()
'data
Cells.Clear
[a2:b7] = [{1,1;1,5;1,13;2,3;2,5;2,6}]
'code
lre = [b2]
For i = 2 To 7
If Cells(i, 1) <> Cells(i - 1, 1) Then
Cells(i, 3) = 1
ElseIf Cells(i, 2) - lre >= 10 Then
Cells(i, 3) = 1
lre = Cells(i, 2)
Else
Cells(i, 3) = 0
End If
Next i
End Sub
It uses the same logic as your formula, but uses the lre variable to store the last real event's date.

Retrieving name and location of specific Shapes from worksheet with VBA

This is a follow up to my previous question (Retrieving information of OLEObjects from Workbook with VBA)
Scenario: I am trying to retrieve data from a worksheet. The data might be normal strings or number or might be encased in check boxed (checked or not).
Data example:
+---------+-------+------------------+------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+------+------------------+
| value x | rfd | checkbox for rfd | nfd | checkbox for nfd |
+---------+-------+------------------+------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+------+------------------+
Obs: In this example the "checkbox for rfd/nfd" is a normal checkbox (either form or activex), and depending on the item in that sheet, either might be selected.
Objective: What I am trying to do is read the worksheet in 2 steps: First read all the data that is directly called, so I use the code:
Sub Test_retrieve()
' this will get all non object values from the sheet
Dim array_test As Variant
Dim i As Long, j As Long
array_test = ThisWorkbook.Sheets(1).UsedRange
For i = 1 To ThisWorkbook.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
For j = 1 To ThisWorkbook.Sheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
ThisWorkbook.Sheets(2).Cells(i, j) = array_test(i, j)
Next j
Next i
End Sub
to get:
+---------+-------+------------------+------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+------+------------------+
| value x | rfd | | nfd | |
+---------+-------+------------------+------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+------+------------------+
Next I am trying to reach all the objectives/shapes in my worksheet. I used the following code to get name, value (checked of not) and location of all activex objects:
Sub getavticeboxvalue()
' this will get the names and values (as binary) of all the activex controlbox objects in the sheet
Dim objx As Object
Dim i As Long
i = 1
For Each objx In ThisWorkbook.Sheets(1).OLEObjects
If objx.Object.Value = True Then
ThisWorkbook.Sheets(3).Cells(i, 1).Value = 1
ThisWorkbook.Sheets(3).Cells(i, 2).Value = objx.Name
ThisWorkbook.Sheets(3).Cells(i, 3).Value = objx.BottomRightCell.Address
ElseIf objx.Object.Value = False Then
ThisWorkbook.Sheets(3).Cells(i, 1).Value = 0
ThisWorkbook.Sheets(3).Cells(i, 2).Value = objx.Name
ThisWorkbook.Sheets(3).Cells(i, 3).Value = objx.BottomRightCell.Address
End If
i = i + 1
Next objx
End Sub
Which yields something like:
+-------+-----------+----------+
| value | name | location |
+-------+-----------+----------+
| 0 | checkbox1 | $C$2 |
+-------+-----------+----------+
| 1 | checkbox2 | $E$2 |
+-------+-----------+----------+
I would then proceed to feed the values (1s and 0s), to the first table, in the place where the checkboxes originally where (location).
Issue: When I try the same procedure for Form Control (instead of activex), I have less options, and although I can look for them (ThisWorkbook.Sheets(1).Shapes.Type = 8) I cannot find their name or location.
Question: Is there a way to find their name and location? Is there a more efficient way to reach the result?
Objective:
+---------+-------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+
| value x | rfd | 0 | nfd | 1 |
+---------+-------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+

How can I set up a button to add & transform data from the clipboard?

I am trying to create a button on an access form that would copy the information of an excel file, and paste it into an access table that looks like this:
An example excel file I'm trying to paste from is
here on this site (click the excel icon).
The problem is that the format on the data of the excel file is completely off, it doesn't align with how I set up my data labels on the Access table. What I want to do is select the Excel data and copy it, then in access use a button that, on click, gets the information from the clipboard, arranges the rows, eliminates extra spaces and arranges columns etc, and puts the information on the Access table in an orderly manner.
I just started learning vba and I have been trying everything I can think of for days now without success, meaning that I am able to get the data in the way I want to into the clipboard but I cannot get it from the clipboard into the access able.
Please help! Thank you!
Code I have so far
Sub cmdCopy_Click()
Dim objData As New MSForms.DataObject
Dim strText As String
Dim i As Long
Dim j As Long
Dim k As Long
Dim m As Long
Dim ComponentNumber As Integer
Dim ComponentText As String
Dim ComponentBlock(100) As Long
Dim ComponentContent(100) As String
Dim ComponentCount As Integer
Dim ComponentStart As Long
Dim ComponentEnd As Long
Dim ComponentLength As Integer
Dim SearchChar As String
Dim Component(100, 2) As Long
Dim LineArray(8000) As String
Dim labname As Integer
Dim TestString As String
Dim ReferenceRangeStart As Integer
Dim Position As String
Dim ColumnDatePosition(6) As Integer
Dim ColumnDateCount As Integer
Dim ComponentBlockLength As Long
Dim PreliminaryArray(10000, 10) As Variant
ComponentCount = 0
'get text from Clipboard
objData.GetFromClipboard
strText = objData.GetText()
' replace double empty lines with single
StrLength = Len(strText)
strText = Replace(strText, Chr(13) & Chr(10) & Chr(13) & Chr(10), Chr(13) &
Chr(10))
For i = 10 To StrLength
If Mid(strText, i, 9) = "Component" Then
ComponentBlock(ComponentCount) = i
ComponentCount = ComponentCount + 1
i = i + 9
End If
Next i
' separate clipboard into component blocks
ComponentStart = 1
For i = 0 To ComponentCount - 1
ComponentContent(i) = Mid(strText, ComponentStart, ComponentBlock(i) - ComponentStart)
ComponentStart = ComponentBlock(i)
Next i
TotalBlocks = i - 1
' determine column spacing
SearchChar = Chr(13) & Chr(10)
ArrayLength = 0
For k = 0 To TotalBlocks
Next k
ComponentLength = InStr(ComponentContent(k), SearchChar) + 1
'Determine where Reference Range starts
ReferenceRangeStart = InStr(1, ComponentContent(k), "Latest")
'Determine position of each date column
ColumnDateCount = 0
For m = 0 To 6
ColumnDatePosition(m) = 0
Next m
ComponentLength = InStr(ComponentContent(k), SearchChar) + 1
'Determine where Reference Range starts
ReferenceRangeStart = InStr(1, ComponentContent(k), "Latest")
'Determine position of each date column
ColumnDateCount = 0
For i = ReferenceRangeStart + 7 To ComponentLength - 10
Position = Mid(ComponentContent(k), i, 6)
If Position Like "##/##/" Then
ColumnDatePosition(ColumnDateCount) = i
i = i + 6
ColumnDateCount = ColumnDateCount + 1
End If
If Position Like "#/##/#" Then
ColumnDatePosition(ColumnDateCount) = i
i = i + 6
ColumnDateCount = ColumnDateCount + 1
End If
If Position Like "#/#/##" Then
ColumnDatePosition(ColumnDateCount) = i
i = i + 6
ColumnDateCount = ColumnDateCount + 1
End If
If Position Like "##/#/#" Then
ColumnDatePosition(ColumnDateCount) = i
i = i + 6
ColumnDateCount = ColumnDateCount + 1
End If
Next i
'Debug.Print ColumnDatePosition(0), ColumnDatePosition(1), ColumnDatePosition(2), ColumnDatePosition(3), ColumnDatePosition(4), ColumnDatePosition(5)
'Length of component line is ComponentLength
'Reference Range starts at ReferenceRangeStart
'ColumnDateCount indicates how many date columns of labs are present
'ColumnDatePosition(ColumnDateCount) stores the date positions
'ComponentContent(ComponentCount) represents the text block for each component block
'Mid(ComponentContent(0), ColumnDatePosition(1), 10), Mid(ComponentContent(0), ColumnDatePosition(2), 10), Mid(ComponentContent(0), ColumnDatePosition(3), 10)
LineLength = Len(ComponentContent(k)) / ComponentLength
For i = 1 To LineLength
If Mid(ComponentContent(k), (i - 1) * ComponentLength + 1, 9) <> "Component"
And Not Asc(Mid(ComponentContent(k), (i - 1) * ComponentLength + 1, 1)) = 32
Then
j = 0
Do While ColumnDatePosition(j + 1) > 0
If Asc(Mid(ComponentContent(k), ColumnDatePosition(j) + (i - 1) * ComponentLength, 1)) <> 32 Then
'ArrayLength,0 is component(lab name)
PreliminaryArray(ArrayLength, 0) = Mid(ComponentContent(k), (i - 1) * ComponentLength + 1, ReferenceRangeStart - 1)
DateLength = ColumnDatePosition(j + 1) - ColumnDatePosition(j)
ReferenceLength = ColumnDatePosition(0) - ReferenceRangeStart
'ArrayLength,1 is reference range
PreliminaryArray(ArrayLength, 1) = Mid(ComponentContent(k), ReferenceRangeStart + (i - 1) * ComponentLength, ReferenceLength)
'ArrayLength,2 is date
PreliminaryArray(ArrayLength, 2) = Mid(ComponentContent(k), ColumnDatePosition(j), 10)
PreliminaryArray(ArrayLength, 2) = CDate(PreliminaryArray(ArrayLength, 2))
'ArrayLength,3 is lab value
PreliminaryArray(ArrayLength, 3) = Mid(ComponentContent(k), ColumnDatePosition(j) + (i - 1) * ComponentLength, DateLength)
ArrayLength = ArrayLength + 1
End If
j = j + 1
Loop
If Asc(Mid(ComponentContent(k), ColumnDatePosition(j) + (i - 1) * ComponentLength, 1)) <> 32 Then
PreliminaryArray(ArrayLength, 0) = Mid(ComponentContent(k), (i - 1) * ComponentLength + 1, ReferenceRangeStart - 1)
PreliminaryArray(ArrayLength, 1) = Mid(ComponentContent(k), ReferenceRangeStart + (i - 1) * ComponentLength, ColumnDatePosition(0) - ReferenceRangeStart)
PreliminaryArray(ArrayLength, 2) = Mid(ComponentContent(k), ColumnDatePosition(j), 10)
PreliminaryArray(ArrayLength, 2) = CDate(PreliminaryArray(ArrayLength, 2))
PreliminaryArray(ArrayLength, 3) = Mid(ComponentContent(k), ColumnDatePosition(j) + (i - 1) * ComponentLength, ComponentLength - ColumnDatePosition(j) - 2)
ArrayLength = ArrayLength + 1
End If
End If
Next i
Changing to a monospaced font reveals that data is transposed into 4 space delimited columns.
Using Range.TextToColumns() to split and WorksheetFunction.Transpose(.UsedRange.Value) to transpose the data, we get a standard table with 11 columns by 3 rows.
+------------------------+--------------+-------------------+-------------------------+-----------+-----------------------+---------------------------+-----------------------------+-------------------------------+----------------------------+----------------+---------------------------+---------------------+--------------------------+-----------------------+------------------------+---------------------+----------------------+-----------------------+-------------------------+-----------------+-----------------------+-----------------+------------------------+----------------------+------------------+---------------+-------------------------+-----------------+----------------------+------------------------+-------------------+----------------------+----------------------+---------------+---------------+--------------+
| Component | Color, Urine | Appearance, Urine | Specific Gravity, Urine | Urine pH | Protein Semiquant, UA | Glucose, Urine, Semiquant | Ketones, Urine, Qualitative | Bilirubin, Urine, Qualitative | Hemoglobin Pigments, Urine | Nitrite, Urine | Leukocyte Esterase, Urine | Urobilinogen, Urine | White Blood Cells, Urine | White Blood Cells, UA | Red Blood Cells, Urine | Red Blood Cells, UA | Hyaline Casts, Urine | Granular Casts, Urine | Epithelial Cells, Urine | Bacteria, Urine | Mucous Threads, Urine | Crystals, Urine | White Blood Cell Count | Red Blood Cell Count | Hemoglobin | Hematocrit | Mean Corpuscular Volume | Mean Corpus Hgb | Mean Corpus Hgb Conc | RBC Distribution Width | Platelet Count | Mean Platelet Volume | Nucleated RBC Number | Neutrophil % | Lymphocytes % | Monocyte % |
+------------------------+--------------+-------------------+-------------------------+-----------+-----------------------+---------------------------+-----------------------------+-------------------------------+----------------------------+----------------+---------------------------+---------------------+--------------------------+-----------------------+------------------------+---------------------+----------------------+-----------------------+-------------------------+-----------------+-----------------------+-----------------+------------------------+----------------------+------------------+---------------+-------------------------+-----------------+----------------------+------------------------+-------------------+----------------------+----------------------+---------------+---------------+--------------+
| Latest Ref Rng & Units | | | 1.003 - 1.030 | 4.6 - 8.0 | Negative | Negative mg/dL | Negative | Negative | Negative | Negative | Negative | 0.2 - 1.0 mg/dL | 0 - 5 /[HPF] | 0 - 27 /uL | 0 - 5 /[HPF] | 0 - 27 /uL | 0 - 1 /[LPF] | None seen /[LPF] | /[HPF] | None-few /[HPF] | None-few /[LPF] | None-few /[HPF] | 4.50 - 11.00 K/cu mm | 4.00 - 5.20 M/cu mm | 12.0 - 15.0 g/dL | 36.0 - 46.0 % | 80.0 - 100.0 fL | 26.0 - 34.0 pg | 31.0 - 37.0 g/dL | 11.5 - 14.5 % | 150 - 350 K/cu mm | 9.2 - 12.7 fL | 0.00 - 0.01 K/cu mm | 40.0 - 70.0 % | 24.0 - 44.0 % | 2.0 - 11.0 % |
| 12/19/2016 | Yellow | Clear | 1.012 | 6 | Negative | Negative | Negative | Negative | Small (A) | Negative | Moderate (A) | <=1.0 | 0 | 1 | 1 | 3 | 0 | 0 | <1 | None | Rare | None | | | | | | | | | | | | | | |
| 1/24/2017 | | | | | | | | | | | | | | | | | | | | | | | 1.82 (L) | 4.71 | 12.6 | 39.3 | 83.4 | 26.8 | 32.1 | 13.1 | 165 | 12.5 | 0 | 42.4 | 37.9 | 16.5 (H) |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+------------------------+--------------+-------------------+-------------------------+-----------+-----------------------+---------------------------+-----------------------------+-------------------------------+----------------------------+----------------+---------------------------+---------------------+--------------------------+-----------------------+------------------------+---------------------+----------------------+-----------------------+-------------------------+-----------------+-----------------------+-----------------+------------------------+----------------------+------------------+---------------+-------------------------+-----------------+----------------------+------------------------+-------------------+----------------------+----------------------+---------------+---------------+--------------+
Code
Sub RealignData()
Dim data As Variant
With Worksheets("Sheet1")
.UsedRange.TextToColumns Destination:=.Range("A1"), DataType:=xlFixedWidth, FieldInfo:=Array(Array(0, 1), Array(79, 1), Array(128, 1), Array(154, 1)), TrailingMinusNumbers:=True
data = WorksheetFunction.Transpose(.UsedRange.Value)
.UsedRange.ClearContents
Range("A1").Resize(UBound(data), UBound(data, 2)).Value = data
End With
End Sub
This should make the data easier to work with. As far as getting it into Access there are better way then copy and paste. I recommend learning how to use Adodb.Recordset.AddNew.

How to count the numeric values from cell

I have values in cell like.
1,2,4,45,64,Jan. Ans: 5
I want to count only number from the cell.
Is it possible?
You could also use the split function in a UDF:
Function CountNumbers(R As Range) As Integer
Dim values, value As Variant
Dim result As Integer
values = Split(R.Text, ",")
result = 0
For Each value In values
If IsNumeric(value) Then
result = result + 1
End If
Next value
CountNumbers = result
End Function
From here, you could call it within another Excel cell as:
=CountNumbers(A1)
Here is my answer:
Sub countingNumbers()
Dim c As Range
Dim L As Long
Dim s As String
Dim tmp
Dim i
Dim y
Set c = Range("A1") 'Imagine in A1 is this: "RDFY2372784GDTD2GV3G3G3V3"
L = Len(c.Value) 'to store the len of the string
s = c.Value 'to store the string
y = 0 'the index of the numbers
For i = 1 To L
tmp = Left(Right(s, i), 1) 'to take just one letter at the time
If Asc(tmp) < 57 And Asc(tmp) > 48 Then
'if is a number ASC() returns the ASCII code of that number
'and increase y one by one || that is number by number
y = y + 1
End If
Next i
Range("B2").Value = y 'store the counting in B2
End Sub
Part of the ASCII Table:
+------------+-----------+
| ASCII Code | Character |
+------------+-----------+
| 48 | 0 |
| 49 | 1 |
| 50 | 2 |
| 51 | 3 |
| 52 | 4 |
| 53 | 5 |
| 54 | 6 |
| 55 | 7 |
| 56 | 8 |
| 57 | 9 |
| 64 | # |
| 65 | A |
| 66 | B |
| 67 | C |
| 68 | D |
| 69 | E |
| 70 | F |
| 71 | G |
| 72 | H |
| 73 | I |
| 74 | J |
+------------+-----------+
Here you can check the ASCII table

Mathematical transpose in excel

Good day everybody!
I'm currently trying to figure something out in excel before implementing in it VBScript. I have to mathematically transpose a few cells (10*10 or 5r*10c) in a matrice:
-------------------------------
| .. | .. | .. | .. | .. | .. |
| 21 | 22 | 23 | 24 | 25 | .. |
| 11 | 12 | 13 | 14 | 15 | .. |
| 1 | 2 | 3 | 4 | 5 | .. |
-------------------------------
Must become
-------------------------------
| .. | .. | .. | .. | .. | .. |
| 3 | 13 | 23 | 33 | 43 | .. |
| 2 | 12 | 22 | 32 | 42 | .. |
| 1 | 11 | 21 | 31 | 41 | .. |
-------------------------------
Now I'm not a mathematician (I'm more ore less a programmer at the moment), but I came up with: F(y)=((MOD(x,10)-1)*10)+(1+((x-MOD(x,10))/10)) (x is the value in the pre-block a the top, y is the value in the pre-block below.) Now this works fine up to a certain point (e.g. 10).
In VBScript, I wrote the below at first:
Function GetPosInSrcRack(Pos)
Dim PlateDef(9), x, y, i, tmp
' Plate Definition
ReDim tmp(UBound(PlateDef))
For x = 0 To UBound(PlateDef)
PlateDef(x) = tmp
Next
i = 1
For x = 0 To UBound(PlateDef)
For y = 0 To UBound(PlateDef(x))
PlateDef(x)(y) = i
i = (i + 1)
Next
Next
'Dim msg ' Check definition
'For x = 0 To (UBound(PlateDef))
' msg = Join(PlateDef(x), ", ") & vbCrLf & msg
'Next
' Get the Position
y = (pos Mod 10)
x = ((pos - y) / 10)
GetPosInSrcRack = PlateDef(y)(x)
End Function
Which, of course, works but is crappy.
Using the above formula I would write:
Function GetPosInSrcRack(Pos)
Pos = (((Pos MOD 10)-1)*10)+(1+((Pos - (Pos MOD 10))/10))
End Function
But like I said, this still is incorrect (10 gives -8)
Can somebody help me?
Just use Paste Special > Transpose option.
y=(MOD(x-1,10))*10+INT((x-1)/10)+1
(By the way, what you are doing is not matrix transposition, but this does do what you do, only better.)

Resources