Căutarea în Matrice – Problemă de #Excel [Updated]

Articolul a fost actualizat pe data de 22.03.2019 Ora 22:00. Soluțiile mai jos.

Salutare tuturor fanilor Excel … care au mai rămas pe aici. Astăzi am primit o problemă interesantă de Excel pe care v-o supun atenției și vouă.

Se dă următoarea matrice:

Valori/Ziua Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9 Z10
O1           A        
O2   A A           A  
O3     B A       A    
O4     C C            
O5   B   B           B
O6 A         C        
O7     D              
O8       D     B     D
O9 C                  
O10           D        

Realizați un raport de sinteză cu următoarea structură și care să aibă rezultatul în formatul următor:

Ziua/Count A B C D
Z1 O6 O9
Z2 O2 O5
Z3 O2 O3 O4 O7
Z4 O3 O5 O4 O8
Z5
Z6 O1 O6 O10
Z7 O8
Z8 O3
Z9 O2
Z10 O5 O8

Soluția trebuie transmisă prin e-mail la adresa: valy.greavu@outlook.com până pe 22.03.2019 ora 22:00. Dacă sunt studenți ai mei în anul I care rezolvă și știu să-mi explice rezolvarea vor primi 10 la examenul de săptămâna viitoare!

Fișierul cu rezolvarea mea poate fi descărcat de aici.

Aștept cu interes!

[UPDATE] Soluții

În decursul celor 30 de ore cât a fost disponibilă problema spre rezolvare, am primit 4 soluții de la oameni extraordinari de buni în Excel, cu unii dintre ei am mai reacționat în timp. Nu știu dacă mai sunt și alții care au mai încercat, dar pe baza numărului de vizualizări, tind să cred că au fost destul de puțini. Îi încurajez pe toți cei care vor să încerce să îmi ceară feedback pe parcurs…

Problema a fost expusă doar pe jumătate. Jumătatea în care fiecare valoare apare doar odată pe coloană. Pentru a rezolva această problemă lucrurile sunt simple. Se folosește o funcție Index combinată cu un match pe un offset dinamic generat de valorile corespunzătoare coloanei pe care se află valoarea.

Soluția mea

În exemplul meu, formula pentru celula B15, echivalentă combinației Z1/A este:

=IFNA(INDEX($A$1:$A$11;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));"")

în care:

formula OFFSET() se folosește pentru a determina coloana de căutare a MATCH-ului pe B$14. Acest offet este determinat de MATCH-ul său intern ($A15) și are numărul echivalent al liniilor determinate de COUNTA.

Pentru varianta cu duplicate (și aici m-am oprit cu formulele… adică nu am formulă pentru mai mult de două valori) treaba se complică foarte mult. Pe lângă faptul că trebuie să continue căutarea trebuie adăugat și un separator de valori (în cazul meu virgulă) care trebuie eliminat din valorile nule sau singulare.

În fișierul care poate fi descărcat mai sus, formula pentru duplicate este de forma:

=IF(
RIGHT(CONCATENATE(IFNA(
INDEX($A$1:$A$11;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));"");",";
IFNA(INDEX(OFFSET($A$1;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0);0;COUNTA($A$1:$A$11);1);
MATCH(B$14;OFFSET($A$1;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0);MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));"") ))=",";

IFNA(INDEX($A$1:$A$11;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));"");

CONCATENATE(IFNA(INDEX($A$1:$A$11;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));"");",";IFNA(INDEX(OFFSET($A$1;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0);0;COUNTA($A$1:$A$11);1);MATCH(B$14;OFFSET($A$1;MATCH(B$14;OFFSET($A$1;0;MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0);MATCH($A15;$A$1:$K$1;0)-1;COUNTA($A$1:$A$11);1);0));""))
)

Problema acestei căutări este dată de determinarea celei de-a doua părți a coloanei de căutare care este diferită de la coloană la coloană în funcție de poziția căutării primei valori din coloană.

Sfat: Dacă vi se pare o virtute să scrii formule ca cea de mai sus în Excel, aflați că răspunsul este NU. Important este să găsești o soluție elegantă și corectă, nu una în care folosești multe litere.

Dacă doriți să revedeți articolul despre funcția OFFSET() vă rog să consultați articolul: https://valygreavu.com/2017/05/08/excel-functia-offset/

Soluțiile primte, nu în ordine.

Soluția lui George

Voi începe cu soluția lui George (fost student al meu) care mi s-a părut deosebit de suprinzătoare prin simplitate și corectitudinea rezolvării pentru valori unice.  Dezavantajul că nu rezolvă duplicatele, dar mie mi-a plăcut.

George folosește o funcție array pentru transpose-ul matricii inițiale, după care folosește un concatenate cu Match() pentru a obține matricea de valori.

Fișierul poate fi descărcat și analizat la adresa: https://1drv.ms/x/s!ApGubfWFh8Nuq_teh5GWrX7vs2A2QQ

Soluții cu Power Query

PowerQuery devine din ce în ce mai prezent în rezolvarea problemelor de Excel legate de manipulare a datelor. Ca exemplu:

  • Soluția de la Alexandru (din București) a fost bazată pe PowerQuery și corectă pentru valori unice pe coloană.
  • Ciprian (din Iași) a propus o soluție bazată pe formula mea inițială și pe PowerQuery care respectă valorile multiple pe coloană.

Explicațiile lui Ciprian pentru rezolvarea sa:

Câtă vreme nu ai spus de la început că vrei și asta nu am pierdut timp să fac și asta…. am respectat 100% ce ai enunțat în problemă, chiar și la faza cu înregistrările unice :)

Oricum asta cu numele coloanelor e simplă:

în pasul 2, înlocuiești lista cu numele coloanelor în clar {„Z1”, „Z2”, „Z3”, „Z4”, „Z5”, „Z6”, „Z7”, „Z8”, „Z9”, „Z10″}) cu numele TUTUROR coloanelor: Table.ColumnNames(Source)

dacă vrei toate coloanele în afară de prima,  atunci înlocuiești cu Text.Split(Text.AfterDelimiter(Text.Combine(Table.ColumnNames(Source),”,”),”,”,1),”,”)

algoritmul e simplu:

let

//Pas.1 – Preiau datele de intare

Source = Excel.CurrentWorkbook(){[Name=”Table1″]}[Content],

//Pas2. – Opțional – conserv zilele care nu au valori, altfel se pierd la repivotare

#”Replaced Value” = Table.ReplaceValue(Source,null,”0″,Replacer.ReplaceValue,Text.Split(Text.AfterDelimiter(Text.Combine(Table.ColumnNames(Source),”,”),”,”,1),”,”)),

//Pas3. – evident

#”Unpivoted Columns” = Table.UnpivotOtherColumns(#”Replaced Value”, {„Valori/Ziua”}, „Ziua”, „Contor”),

//Pas4. Opțional – … mă deranja numele neconform conținutului :)

#”Renamed Columns” = Table.RenameColumns(#”Unpivoted Columns”,{{„Valori/Ziua”, „Valori”}}),

//Pas5. grupez înregistrările multiple. după dorință se poate renunța și în pivot iei primul, ultimul, minim, max, media, mediana, etc.

#”Grouped Rows” = Table.Group(#”Renamed Columns”, {„Contor”, „Ziua”}, {{„Valori_Combinate”, each Text.Combine(_[Valori],”,”), type text}}),

//Pas6. repivotez datele

#”Pivoted Column” = Table.Pivot(#”Grouped Rows”, List.Distinct(#”Grouped Rows”[Contor]), „Contor”, „Valori_Combinate”),

//Pas7. opțional de două ori: elimin coloana pentru null. 1. Poate fi arătată dacă interesează pe cineva. 2. dacă se renunță la 2 dispare și ea

#”Removed Columns” = Table.RemoveColumns(#”Pivoted Column”,{„0”}),

//Pas8. Opțional pentru că denumirea coloanelor nu permite ordonarea lexicografică, creez o coloană care să poată fi sortată după cum mă aștept

#”Added Custom” = Table.AddColumn(#”Removed Columns”, „Sort”, each Text.Combine({Text.At([Ziua],0),Text.End(Text.Combine({„0”,Text.Middle([Ziua],1)}),2)})),

//Pas9. opțional – sort

#”Sorted Rows” = Table.Sort(#”Added Custom”,{{„Sort”, Order.Ascending}}),

//Pas10. opțional – elimin coloana de sortare

#”Removed Columns1″ = Table.RemoveColumns(#”Sorted Rows”,{„Sort”})

in

#”Removed Columns1″

Practic pentru a obține același lucru ca în formulele din Excel ai nevoie de 3 pași, pentru a aduce valorile multiple 4, iar restul sunt de floricele

Soluția poate fi descărcată și analizată aici: https://1drv.ms/x/s!ApGubfWFh8Nuq_tgh5GWrX7vs2A2QQ

Soluția cu DAX

Am alocat un subcapitol pentru soluția lui Cristian din Timișoara. Asta pentru că folosește DAX (Data Analysis Expressions) în rezolvarea problemei, pe lângă faptul că o rezolvă și cu PowerQuery. Recunosc că m-am simțit puțin încurcat de povestea cu DataModel și limbajul specific:

Data:=VAR __DISTINCT_VALUES_COUNT = DISTINCTCOUNT(‘DAX'[Value])
VAR __MAX_VALUES_TO_SHOW = 10
RETURN
IF(
__DISTINCT_VALUES_COUNT > __MAX_VALUES_TO_SHOW;
CONCATENATE(
CONCATENATEX(
TOPN(__MAX_VALUES_TO_SHOW; VALUES(‘DAX'[Value]); ‘DAX'[Value]; ASC);
‘DAX'[Value];
„, „;
‘DAX'[Value];
ASC
);
„, etc.”
);
CONCATENATEX(VALUES(‘DAX'[Value]); ‘DAX'[Value]; „, „; ‘DAX'[Value]; ASC)
)

Deci cum?! :)

Soluția lui Cristian poate fi descărcată aici: https://1drv.ms/x/s!ApGubfWFh8Nuq_tih5GWrX7vs2A2QQ

Fișierul conține și un mic macro, dar e safe.

Concluzii

Problema rămâne deschisă. Cred că e prea faină ca să nu mai continuăm să căutăm soluții. Folosiți partea de comentarii pentru asta.

Dacă v-au plăcut soluțiile nu ezitați să ne oferiți feedback în partea de comentarii.

Dacă mai aveți probleme faine spre rezolvare nu ezitați să le trimiteți pe mail.

Sper să fie util cuiva.

Display HTML calculated column KPI in on-premises SharePoint Lists

The June 2017 PU and subsequent PUs will include a new web application setting that’s called CustomMarkupInCalculatedFieldDisabled. This setting lets an on-premises administrator determine whether execution of custom markup in calculated fields in a given web application is blocked. (Source)

If you want to continue to render HTML code in calculated columns you have to use different methods. Almost all web references are using JSLink property. This article describes a way to use XSL to display custom HTML code in SharePoint Lists.

Step by step procedure:

  1. Open the list
  2. Settings and Edit Page
  3. Webpart properties and Edit WebPart
  4. Open Miscellaneous
  5. Type the following code in XSL Link
    <xsl:template match="/" xmlns:x="http://www.w3.org/2001/XMLSchema" />
  6. Click Ok
  7. Stop Editing page
  8. Repeat procedure (1-7) for all list views.

References:

#Excel – Eliminare diacritice din nume – Funcția SUBSTITUTE()

 

Funcția SUBSTITUTE() poate foarte utilă pentru corectarea textului în operațiunile de import sau după caz.

În multe cazuri am văzut oameni (inclusiv pe mine) care fac operațiunile de înlocuire a caracterelor cu ajutorul operațiunii Office Find and Replace (Ctrl+H).

Conform articolului citat mai sus Substitute() se folosește pentru a înlocui toate caracterele de un anumit fel cu alte caractere sau chiar a le elimina.

În exemplul de mai jos avem un caz concret. Caracterele românești sunt din ce în ce mai des folosite (inclusiv în documentele oficiale :) ) dar există suficient de multe probleme legate de sistemele de e-mail de exemplu sau de autentificare. Poate greșesc și forțez puțin cazul de utilizare. Pentru corecturi/completări vă rog folosiți cu încredere secțiunea comentarii.

În exemplul nostru ne este solicitată crearea automată a unui nume de utilizator de maxim 20 de caractere care să fie de forma prenume.nume.

Pentru acest lucru avem nevoie de mai multe funcții SUBSTITUTE() imbricate.

Alternativele ca formule la substitute() sunt combinațiile de funcții FIND() sau SEARCH() și REPLACE(). Nu uitați că funcția FIND() este case sensitive. De asemenea, returnează eroare dacă nu găsește caracterul în textul de căutare.

Problema cu funcția Replace() este că înlocuiește doar primul caracter întâlnit. Pe când Substitute() le înlocuiește pe toate.

Iată un exemplu în care căutăm un caracter cu Find() și îl înlocuim cu replace() și unul cu o formula substitute()

 

În exemplul nostru de început, pentru a înlocui toate diacriticele, spațiul sau liniuța am imbricat mai multe funcții formula din C3 fiind:

=LEFT(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(LOWER(TRIM(B3));”-„;””);”ă”;”a”);”î”;”i”);”ș”;”s”);”ț”;”t”);”â”;”a”);” „;””)&”.”&SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(LOWER(TRIM(A3));”-„;””);”ă”;”a”);”î”;”i”);”ș”;”s”);”ț”;”t”);”â”;”a”);” „;””);20)

Puteți descărca fișierul Excel cu acest exemplu de la adresa: https://1drv.ms/x/s!ApGubfWFh8Nuq_pgh5GWrX7vs2A2QQ

Sper să fie util cuiva! Nu uitați de comentarii pentru întrebări.

Recomandare de citit: Cele mai eficiente zece metode de curățare a datelor

Blog la WordPress.com.

SUS ↑